@aglyn/plugins-ai 1.0.0-beta.285 → 1.0.0-beta.288
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 +17 -17
- package/src/lib/assets/site-styles/nonprofit.webp +0 -0
- package/src/lib/assets/site-styles/professional.webp +0 -0
- package/src/lib/assets/site-styles/store.webp +0 -0
- package/src/lib/assets/site-styles/trades.webp +0 -0
- package/src/lib/components/ai-theme-proposal-card.component.js +12 -3
- package/src/lib/components/ai-theme-proposal-card.component.js.map +1 -1
- package/src/lib/declarations.js +5 -2
- package/src/lib/declarations.js.map +1 -1
- package/src/lib/declarations.server.js +12 -0
- package/src/lib/declarations.server.js.map +1 -1
- package/src/lib/jobs/ai-build-capabilities.d.ts +3 -1
- package/src/lib/jobs/ai-build-capabilities.js +27 -2
- package/src/lib/jobs/ai-build-capabilities.js.map +1 -1
- package/src/lib/jobs/ai-business-profile-prefill.js +6 -3
- package/src/lib/jobs/ai-business-profile-prefill.js.map +1 -1
- package/src/lib/jobs/ai-image-reads.d.ts +59 -0
- package/src/lib/jobs/ai-image-reads.js +138 -0
- package/src/lib/jobs/ai-image-reads.js.map +1 -0
- package/src/lib/jobs/ai-job-build-step.d.ts +29 -0
- package/src/lib/jobs/ai-job-build-step.js +83 -12
- package/src/lib/jobs/ai-job-build-step.js.map +1 -1
- package/src/lib/jobs/ai-job-duplicate-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-duplicate-step.js +6 -6
- package/src/lib/jobs/ai-job-duplicate-step.js.map +1 -1
- package/src/lib/jobs/ai-job-import-step.d.ts +4 -2
- package/src/lib/jobs/ai-job-import-step.js +15 -11
- package/src/lib/jobs/ai-job-import-step.js.map +1 -1
- package/src/lib/jobs/ai-job-insight-step.d.ts +10 -0
- package/src/lib/jobs/ai-job-insight-step.js +23 -5
- package/src/lib/jobs/ai-job-insight-step.js.map +1 -1
- package/src/lib/jobs/ai-job-layout-language.js +2 -1
- package/src/lib/jobs/ai-job-layout-language.js.map +1 -1
- package/src/lib/jobs/ai-job-media-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-media-step.js +6 -8
- package/src/lib/jobs/ai-job-media-step.js.map +1 -1
- package/src/lib/jobs/ai-job-navigation-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-navigation-step.js +7 -9
- package/src/lib/jobs/ai-job-navigation-step.js.map +1 -1
- package/src/lib/jobs/ai-job-page-language.js +7 -1
- package/src/lib/jobs/ai-job-page-language.js.map +1 -1
- package/src/lib/jobs/ai-job-page-sections.js +16 -3
- package/src/lib/jobs/ai-job-page-sections.js.map +1 -1
- package/src/lib/jobs/ai-job-page-step.js +9 -1
- package/src/lib/jobs/ai-job-page-step.js.map +1 -1
- package/src/lib/jobs/ai-job-plan-step.d.ts +30 -8
- package/src/lib/jobs/ai-job-plan-step.js +78 -13
- package/src/lib/jobs/ai-job-plan-step.js.map +1 -1
- package/src/lib/jobs/ai-job-privacy-step.d.ts +4 -2
- package/src/lib/jobs/ai-job-privacy-step.js +14 -15
- package/src/lib/jobs/ai-job-privacy-step.js.map +1 -1
- package/src/lib/jobs/ai-job-products-step.js +5 -3
- package/src/lib/jobs/ai-job-products-step.js.map +1 -1
- package/src/lib/jobs/ai-job-settings-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-settings-step.js +7 -8
- package/src/lib/jobs/ai-job-settings-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-content.d.ts +34 -1
- package/src/lib/jobs/ai-job-site-content.js +67 -8
- package/src/lib/jobs/ai-job-site-content.js.map +1 -1
- package/src/lib/jobs/ai-job-site-datasets.d.ts +53 -3
- package/src/lib/jobs/ai-job-site-datasets.js +300 -30
- package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
- package/src/lib/jobs/ai-job-site-step.js +6 -3
- package/src/lib/jobs/ai-job-site-step.js.map +1 -1
- package/src/lib/jobs/ai-job-site-store-pages.js +3 -3
- package/src/lib/jobs/ai-job-site-store-pages.js.map +1 -1
- package/src/lib/jobs/ai-job-text-step.js +2 -1
- package/src/lib/jobs/ai-job-text-step.js.map +1 -1
- package/src/lib/jobs/ai-job-tracking-step.d.ts +2 -1
- package/src/lib/jobs/ai-job-tracking-step.js +7 -8
- package/src/lib/jobs/ai-job-tracking-step.js.map +1 -1
- package/src/lib/jobs/ai-jobs-beat.d.ts +11 -7
- package/src/lib/jobs/ai-jobs-beat.js +18 -8
- package/src/lib/jobs/ai-jobs-beat.js.map +1 -1
- package/src/lib/jobs/ai-jobs-notify.d.ts +66 -1
- package/src/lib/jobs/ai-jobs-notify.js +136 -3
- package/src/lib/jobs/ai-jobs-notify.js.map +1 -1
- package/src/lib/jobs/ai-layout-stock-photos.d.ts +155 -7
- package/src/lib/jobs/ai-layout-stock-photos.js +1517 -220
- package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
- package/src/lib/jobs/ai-product-image.d.ts +14 -0
- package/src/lib/jobs/ai-product-image.js +73 -21
- package/src/lib/jobs/ai-product-image.js.map +1 -1
- package/src/lib/jobs/ai-theme-brand-inputs.d.ts +9 -1
- package/src/lib/jobs/ai-theme-brand-inputs.js +28 -2
- package/src/lib/jobs/ai-theme-brand-inputs.js.map +1 -1
- package/src/lib/jobs/fixtures/ai-site-agl4065-datasets.d.ts +175 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-datasets.js +601 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-datasets.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-pages.d.ts +46 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-pages.js +2862 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-pages.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-plans.d.ts +74 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-plans.js +888 -0
- package/src/lib/jobs/fixtures/ai-site-agl4065-plans.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-site-agl4092-datasets.d.ts +120 -0
- package/src/lib/jobs/fixtures/ai-site-agl4092-datasets.js +410 -0
- package/src/lib/jobs/fixtures/ai-site-agl4092-datasets.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-judge.d.ts +29 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-judge.js +81 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-judge.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-recordings.d.ts +87 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-recordings.js +225 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4065-recordings.js.map +1 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-recordings.d.ts +26 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-recordings.js +714 -0
- package/src/lib/jobs/fixtures/ai-stock-photo-agl4092-recordings.js.map +1 -0
- package/src/lib/layout-language/ai-layout-compiler.d.ts +11 -0
- package/src/lib/layout-language/ai-layout-compiler.js +194 -18
- package/src/lib/layout-language/ai-layout-compiler.js.map +1 -1
- package/src/lib/layout-language/ai-layout-copy.d.ts +37 -0
- package/src/lib/layout-language/ai-layout-copy.js +383 -3
- package/src/lib/layout-language/ai-layout-copy.js.map +1 -1
- package/src/lib/layout-language/ai-layout-design.d.ts +19 -0
- package/src/lib/layout-language/ai-layout-design.js +31 -11
- package/src/lib/layout-language/ai-layout-design.js.map +1 -1
- package/src/lib/layout-language/ai-layout-listings.d.ts +7 -1
- package/src/lib/layout-language/ai-layout-listings.js +9 -1
- package/src/lib/layout-language/ai-layout-listings.js.map +1 -1
- package/src/lib/layout-language/ai-layout-pictures.d.ts +39 -1
- package/src/lib/layout-language/ai-layout-pictures.js +141 -4
- package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
- package/src/lib/layout-language/ai-layout-storefront.d.ts +18 -4
- package/src/lib/layout-language/ai-layout-storefront.js +100 -20
- package/src/lib/layout-language/ai-layout-storefront.js.map +1 -1
- package/src/lib/model/ai-build-job.d.ts +7 -0
- package/src/lib/model/ai-build-job.js +36 -7
- package/src/lib/model/ai-build-job.js.map +1 -1
- package/src/lib/model/ai-build-plan.d.ts +28 -4
- package/src/lib/model/ai-build-plan.js +49 -9
- package/src/lib/model/ai-build-plan.js.map +1 -1
- package/src/lib/model/ai-build-progress.js +3 -1
- package/src/lib/model/ai-build-progress.js.map +1 -1
- package/src/lib/model/ai-job-notice.d.ts +8 -0
- package/src/lib/model/ai-job-notice.js +33 -0
- package/src/lib/model/ai-job-notice.js.map +1 -1
- package/src/lib/model/ai-job-plan-lines.d.ts +2 -1
- package/src/lib/model/ai-job-plan-lines.js +6 -1
- package/src/lib/model/ai-job-plan-lines.js.map +1 -1
- package/src/lib/model/ai-jobs.types.d.ts +8 -0
- package/src/lib/model/ai-jobs.types.js.map +1 -1
- package/src/lib/model/ai-permissions.d.ts +13 -0
- package/src/lib/model/ai-permissions.js +29 -1
- package/src/lib/model/ai-permissions.js.map +1 -1
- package/src/lib/model/ai-plan-said-sections.d.ts +12 -3
- package/src/lib/model/ai-plan-said-sections.js +22 -5
- package/src/lib/model/ai-plan-said-sections.js.map +1 -1
- package/src/lib/model/ai-products.d.ts +18 -0
- package/src/lib/model/ai-products.js +32 -0
- package/src/lib/model/ai-products.js.map +1 -1
- package/src/lib/model/ai-site-context.d.ts +6 -2
- package/src/lib/model/ai-site-context.js +8 -2
- package/src/lib/model/ai-site-context.js.map +1 -1
- package/src/lib/model/ai-site-inspiration.d.ts +3 -2
- package/src/lib/model/ai-site-inspiration.js +3 -2
- package/src/lib/model/ai-site-inspiration.js.map +1 -1
- package/src/lib/plugin.js +4 -13
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/providers/anthropic.js +9 -2
- package/src/lib/providers/anthropic.js.map +1 -1
- package/src/lib/providers/catalog.js +14 -5
- package/src/lib/providers/catalog.js.map +1 -1
- package/src/lib/providers/contract.d.ts +24 -1
- package/src/lib/providers/contract.js.map +1 -1
- package/src/lib/providers/routing.js +5 -2
- package/src/lib/providers/routing.js.map +1 -1
- package/src/lib/runtime/ai-dataset-generation.d.ts +120 -1
- package/src/lib/runtime/ai-dataset-generation.js +352 -3
- package/src/lib/runtime/ai-dataset-generation.js.map +1 -1
- package/src/lib/runtime/ai-dev-replay.d.ts +1 -0
- package/src/lib/runtime/ai-dev-replay.js +4 -2
- package/src/lib/runtime/ai-dev-replay.js.map +1 -1
- package/src/lib/runtime/ai-doctrine-validators.d.ts +47 -3
- package/src/lib/runtime/ai-doctrine-validators.js +345 -28
- package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
- package/src/lib/runtime/ai-doctrine.d.ts +5 -4
- package/src/lib/runtime/ai-doctrine.js +10 -3
- package/src/lib/runtime/ai-doctrine.js.map +1 -1
- package/src/lib/runtime/ai-icon-library.d.ts +27 -1
- package/src/lib/runtime/ai-icon-library.js +30 -3
- package/src/lib/runtime/ai-icon-library.js.map +1 -1
- package/src/lib/runtime/ai-products-generation.d.ts +11 -1
- package/src/lib/runtime/ai-products-generation.js +51 -4
- package/src/lib/runtime/ai-products-generation.js.map +1 -1
- package/src/lib/runtime/assist-answer-cache.d.ts +2 -0
- package/src/lib/runtime/assist-answer-cache.js +5 -2
- package/src/lib/runtime/assist-answer-cache.js.map +1 -1
- package/src/lib/server/ai-allotments.js +7 -2
- package/src/lib/server/ai-allotments.js.map +1 -1
- package/src/lib/server/ai-experiment-versions.js +5 -5
- package/src/lib/server/ai-experiment-versions.js.map +1 -1
- package/src/lib/server/ai-host-permissions-roster.d.ts +25 -0
- package/src/lib/server/ai-host-permissions-roster.js +33 -0
- package/src/lib/server/ai-host-permissions-roster.js.map +1 -0
- package/src/lib/server/ai-host-permissions.js +8 -6
- package/src/lib/server/ai-host-permissions.js.map +1 -1
- package/src/lib/server/ai-image-reads-route.d.ts +17 -0
- package/src/lib/server/ai-image-reads-route.js +106 -0
- package/src/lib/server/ai-image-reads-route.js.map +1 -0
- package/src/lib/server/ai-seo-apply.js +5 -5
- package/src/lib/server/ai-seo-apply.js.map +1 -1
- package/src/lib/server/ai-site-import-route.js +7 -4
- package/src/lib/server/ai-site-import-route.js.map +1 -1
- package/src/lib/server/assist-build.d.ts +15 -1
- package/src/lib/server/assist-build.js +30 -3
- package/src/lib/server/assist-build.js.map +1 -1
- package/src/lib/server/assist-chat.d.ts +11 -7
- package/src/lib/server/assist-chat.js +185 -48
- package/src/lib/server/assist-chat.js.map +1 -1
- package/src/lib/server/assist-docs-query.d.ts +51 -0
- package/src/lib/server/assist-docs-query.js +57 -0
- package/src/lib/server/assist-docs-query.js.map +1 -0
- package/src/lib/server/assist-jobs-context.d.ts +102 -0
- package/src/lib/server/assist-jobs-context.js +227 -0
- package/src/lib/server/assist-jobs-context.js.map +1 -0
- package/src/lib/server/assist-view-context.d.ts +8 -4
- package/src/lib/server/assist-view-context.js +12 -7
- package/src/lib/server/assist-view-context.js.map +1 -1
- package/src/lib/server.js +12 -0
- package/src/lib/server.js.map +1 -1
- package/src/lib/tools/ai-dataset-tool.d.ts +11 -1
- package/src/lib/tools/ai-dataset-tool.js +24 -7
- package/src/lib/tools/ai-dataset-tool.js.map +1 -1
- package/src/lib/tools/ai-duplicate-resource-tool.js +5 -6
- package/src/lib/tools/ai-duplicate-resource-tool.js.map +1 -1
- package/src/lib/components/ai-collaborator-permissions-column.component.d.ts +0 -32
- package/src/lib/components/ai-collaborator-permissions-column.component.js +0 -132
- package/src/lib/components/ai-collaborator-permissions-column.component.js.map +0 -1
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ import { _ as _extends } from "@swc/helpers/_/_extends";
|
|
17
|
+
import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
|
|
17
18
|
import { AI_ROUTING_TABLE } from "../providers/routing.js";
|
|
18
19
|
import { AI_DATASET_LIMITS, AI_DATASET_LIST_SEPARATOR, AI_DATASET_TOOL, AI_DATASET_TOOL_NAME, checkAiDataset } from "../tools/ai-dataset-tool.js";
|
|
19
20
|
import { runValidatedGeneration } from "./ai-doctrine.js";
|
|
20
21
|
import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
22
|
+
import { aiTextNamesBrand } from "../model/ai-products.js";
|
|
21
23
|
/**
|
|
22
24
|
* A SITE'S DATASETS (AGL-3616): the typed fields and first records of one
|
|
23
25
|
* dataset a site plan created, through the doctrine's generation call and the
|
|
@@ -37,6 +39,17 @@ import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
|
37
39
|
* with room; under the routing row's.
|
|
38
40
|
*/ export const AI_DATASET_MAX_TOKENS = Math.min(4000, AI_ROUTING_TABLE[AI_DATASET_STEP].maxTokens);
|
|
39
41
|
/** The generation kind a dataset is asked under, which the doctrine scopes. */ export const AI_DATASET_KIND = 'dataset';
|
|
42
|
+
/**
|
|
43
|
+
* THE Q2 DECISION (AGL-4065): what a list the brief gives no entries for starts
|
|
44
|
+
* with. `samples` (decided 2026-10-28): complete-looking SAMPLE records the
|
|
45
|
+
* owner replaces, each marked a sample in Data, a dated list's dates set by
|
|
46
|
+
* the platform (`aiDatasetSampleDates`), so the site looks complete and no
|
|
47
|
+
* row is a placeholder. `empty`: the fields and no records, and the pages
|
|
48
|
+
* say the entries are on the way. The rules, the tool's answer and the
|
|
49
|
+
* pages' brief all follow this one switch.
|
|
50
|
+
*/ export const AI_DATASET_LIST_WITHOUT_ENTRIES = 'samples';
|
|
51
|
+
/** What a list the brief gives no entries for is told to hold (AGL-4065). */ const WITHOUT_ENTRIES_RULE = AI_DATASET_LIST_WITHOUT_ENTRIES === 'samples' ? 'Where the brief gives no entries for the list, write sample records the owner replaces, each marked sample: a complete entry of its kind, named in the brief’s own words (such as "Series Round 1 at Mill Creek" for a race team that rides at Mill Creek), its other values typical of it. This is the one place a record may give a value the brief does not, and a sample still gives no price, phone number, email, address, real person’s name or brand. Leave a date "" in a sample, for the platform to set, and name no month or date in it. ' + // AGL-4092: six sample riders' bios ended "Add this rider's story here.", and six past races' results were "Add finishing position".
|
|
52
|
+
'A sample’s words read as finished copy about it: never an instruction to the owner, such as "Add this rider\'s story here" or "Add finishing position", and never a note that it is a sample, which the console says.' : 'Where the brief gives no entries for the list, give the fields and no records: an empty list is an answer, a numbered placeholder is not.';
|
|
40
53
|
/** The rules for one dataset. Byte-identical on every request. */ export const AI_DATASET_INSTRUCTIONS = [
|
|
41
54
|
{
|
|
42
55
|
text: 'You design one dataset of a new website: a list of like things — a menu, a team, services, portfolio pieces, events, questions and answers — kept as records the owner edits in one place, which the site’s pages list.\n\n' + `Answer by calling ${AI_DATASET_TOOL_NAME} exactly once. A reply in prose cannot be used.\n\n` + 'Rules:\n' + [
|
|
@@ -44,6 +57,8 @@ import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
|
44
57
|
'Type each field: text for words, including a date or a time written the way a visitor reads it; number or integer only for a figure a visitor compares; boolean for yes or no; list for a few short tags. The first field names each record and is text.',
|
|
45
58
|
`Write ${AI_DATASET_LIMITS.recordsMin} to ${AI_DATASET_LIMITS.recordsMax} records, as many as the brief supports: what the business actually offers, in its own words where the brief gives them, otherwise plain and general enough to be true of it.`,
|
|
46
59
|
'Never invent a fact: no price, date, address, phone number, award, credential, client or number the brief does not give, and no review, testimonial, rating or quote from anyone. Leave a value "" rather than make one up.',
|
|
60
|
+
// AGL-4065: a race schedule with no dates came back as ten numbered placeholders.
|
|
61
|
+
WITHOUT_ENTRIES_RULE,
|
|
47
62
|
'Never give a person a name the brief does not give: a team member the brief does not name is their role, such as "Head chef".',
|
|
48
63
|
'Never say anything cures, treats, heals or prevents anything, and never promise a health, financial or legal result.',
|
|
49
64
|
`Values are plain text with no links, markup, emoji or square brackets, at most ${AI_DATASET_LIMITS.valueMax} characters each; a description is one to three sentences.`,
|
|
@@ -52,6 +67,14 @@ import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
|
52
67
|
cacheBreakpoint: true
|
|
53
68
|
}
|
|
54
69
|
];
|
|
70
|
+
/** Names as a sentence lists them: “A”, “B” and “C”. */ function listed(names, last) {
|
|
71
|
+
var _quoted_;
|
|
72
|
+
const quoted = names.map((name)=>`“${name}”`);
|
|
73
|
+
return quoted.length > 1 ? `${quoted.slice(0, -1).join(', ')} ${last} ${quoted[quoted.length - 1]}` : (_quoted_ = quoted[0]) != null ? _quoted_ : '';
|
|
74
|
+
}
|
|
75
|
+
/** What a dataset is told of the sections that each list part of it (AGL-4065). */ export function aiDatasetSplitLine(split) {
|
|
76
|
+
return `Sections ${listed(split.sections, 'and')} list this dataset: its “${split.field}” field takes exactly ${listed(split.values, 'or')}.`;
|
|
77
|
+
}
|
|
55
78
|
/** The user turn: the site's words, the dataset, and where it is shown. */ export function aiDatasetPrompt(input) {
|
|
56
79
|
return [
|
|
57
80
|
'Brief:',
|
|
@@ -59,9 +82,328 @@ import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
|
59
82
|
`Dataset: “${input.name}” — ${input.why.trim() || 'the site’s structured content'}`,
|
|
60
83
|
`Planned fields: ${input.fields.length ? input.fields.map((field)=>`“${field}”`).join(', ') : '(none: choose them)'}`,
|
|
61
84
|
input.shownIn.length ? `Listed on: ${input.shownIn.join('; ')}.` : 'Listed on the site’s pages.',
|
|
85
|
+
...input.split ? [
|
|
86
|
+
aiDatasetSplitLine(input.split)
|
|
87
|
+
] : [],
|
|
62
88
|
input.recordPages ? 'Each record also gets a page of its own, so give each one enough to read: a sentence or two describing it.' : 'Each record is shown as a card, so keep its values short.'
|
|
63
89
|
].join('\n');
|
|
64
90
|
}
|
|
91
|
+
// ── No placeholder rows (AGL-4065) ─────────────────────────────────────
|
|
92
|
+
/**
|
|
93
|
+
* The re-ask a dataset whose records are placeholders is answered with
|
|
94
|
+
* (AGL-4065): the 2026-10-25 Rut Runners Racing start's "Race events" came
|
|
95
|
+
* back as "Upcoming race 1 (details to be added)" to "Past race 5 (details to
|
|
96
|
+
* be added)", because the rules forbid inventing a date and the first field
|
|
97
|
+
* may not be empty. And (AGL-4092) the 2026-10-28 start's sample riders each
|
|
98
|
+
* ended "Add this rider's story here.": an instruction to the owner is a
|
|
99
|
+
* placeholder too.
|
|
100
|
+
*/ export const AI_DATASET_PLACEHOLDER_REASK = AI_DATASET_LIST_WITHOUT_ENTRIES === 'samples' ? "Every record names a real thing the brief gives, or is a sample marked sample: a complete entry of its kind in the brief's own words. Never a placeholder such as 'Upcoming race 1 (details to be added)', 'TBD', 'coming soon' or 'Item 1'. " + // AGL-4092: the samples' bios and results told the owner what to add.
|
|
101
|
+
"Never an instruction to the owner either, such as 'Add this rider's story here' or 'Add finishing position': a sample's words read as finished copy about the sample." : "Every record names a real thing the brief gives. Where the brief gives no entries for this list — no dates, no names, no results — answer with the fields and ZERO records; never number placeholders like 'Upcoming race 1 (details to be added)'.";
|
|
102
|
+
/** The code a dataset's placeholder records are refused under. */ export const AI_DATASET_PLACEHOLDER_CODE = 'placeholder-records';
|
|
103
|
+
/** A value that marks a gap for later rather than saying anything: in a name, a date or anywhere else. */ const PLACEHOLDER_TEXT = /\b(?:details?|dates?|info|information|names?)\s+(?:(?:to be|will be)\s+)?(?:added|confirmed|announced|determined|decided|filled)\b|\bto be (?:added|confirmed|announced|determined|decided)\b|\b(?:tbd|tba|t\.b\.[ad])(?![a-z])|\bcoming soon\b|\bplaceholder\b|\blorem\b|\b(?:sample|example|test|dummy) (?:record|entry|row|item|name)s?\b/i;
|
|
104
|
+
/** What a sentence tells the owner to do to the record, in place of saying anything: add, write, replace. */ const OWNER_VERB = '(?:add|insert|write|enter|put|type|paste|replace|swap|fill in|fill out|update|describe|upload|attach)';
|
|
105
|
+
/** How an instruction opens: its own words, imperative, first. */ const INSTRUCTION_START = '^\\s*(?:please\\s+)?';
|
|
106
|
+
/**
|
|
107
|
+
* Words that tell the owner to do something, where the record should say
|
|
108
|
+
* something (AGL-4092): "Add this rider's story here.", "Replace with your
|
|
109
|
+
* own photo.", "Update the results later." Only an imperative is one: "Riders
|
|
110
|
+
* enter the gate early and race later" says something, and a visitor's
|
|
111
|
+
* instruction names no place the owner fills ("Fill in the form on the
|
|
112
|
+
* Contact page"); both are kept.
|
|
113
|
+
*/ const OWNER_INSTRUCTION = new RegExp(`${INSTRUCTION_START}(?:${OWNER_VERB}\\b[^.!?]{0,80}?\\b(?:here|later|soon|in data)\\b|(?:replace|swap)\\b[^.!?]{0,60}?\\byour (?:own|real)\\b)`, 'i');
|
|
114
|
+
/** What a record says of itself, which an instruction names as the thing to add: "Add finishing position". */ const RECORD_PARTS = '(?:position|placing|placement|result|results|score|scores|time|times|finish|date|dates|details?|info|information|name|names|story|bio|biography|description|photo|photos|picture|image|link|price|location|address|phone|email|text|copy|content|notes?|summary|title|caption|quote)';
|
|
115
|
+
/** Words that are nothing but a bare instruction naming a part of the record: "Add finishing position", "Enter result". */ const BARE_INSTRUCTION = new RegExp(`${INSTRUCTION_START}${OWNER_VERB}\\s+(?:(?:the|this|that|a|an|their|its|each)\\s+)?(?:[\\p{L}'’-]+\\s+){0,2}${RECORD_PARTS}\\s*[.!]?\\s*$`, 'iu');
|
|
116
|
+
/** An aside in parentheses: "Junior rider (add details later)". */ const ASIDE = /\s*\(([^()]{1,160})\)/g;
|
|
117
|
+
/** Where a sentence turns to something else: a dash, a colon, a semicolon. */ const TURN = /\s*(?:[—–:;]|\s-)\s*/g;
|
|
118
|
+
/** A value's sentences, each with its closing mark. */ const sentencesOf = (value)=>value.split(/(?<=[.!?])\s+/).map((sentence)=>sentence.trim()).filter(Boolean);
|
|
119
|
+
/** What a visitor's instruction points at, which an owner's never does: "Fill in the form here", "Book on the Contact page". */ const VISITOR_TARGET = /\b(?:form|button|link|page|website|checkout|cart|account|app)s?\b/i;
|
|
120
|
+
/** Whether words, read from their start, are an instruction to the owner rather than words about the record (AGL-4092). */ export function aiDatasetInstructionSentence(words) {
|
|
121
|
+
return OWNER_INSTRUCTION.test(words) && !VISITOR_TARGET.test(words) || BARE_INSTRUCTION.test(words);
|
|
122
|
+
}
|
|
123
|
+
/** One sentence without the instruction it is or ends with: "" for "Add finishing position", "Junior rider" for "Junior rider — add details later". */ function sentenceWithoutInstruction(sentence) {
|
|
124
|
+
if (aiDatasetInstructionSentence(sentence)) return '';
|
|
125
|
+
for (const turn of sentence.matchAll(TURN)){
|
|
126
|
+
var _turn_index;
|
|
127
|
+
const at = (_turn_index = turn.index) != null ? _turn_index : 0;
|
|
128
|
+
if (at > 0 && aiDatasetInstructionSentence(sentence.slice(at + turn[0].length))) {
|
|
129
|
+
const head = sentence.slice(0, at).replace(/[\s,]+$/, '');
|
|
130
|
+
return head && !/[.!?]$/.test(head) ? `${head}.` : head;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return sentence;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* A value with every instruction to the owner taken out (AGL-4092): an aside,
|
|
137
|
+
* a sentence, or the turn a sentence ends on; `''` where nothing else was
|
|
138
|
+
* said, and the value unchanged where none was. Pure.
|
|
139
|
+
*/ export function aiDatasetValueWithoutInstructions(value) {
|
|
140
|
+
const unasided = value.replace(ASIDE, (aside, inner)=>aiDatasetInstructionSentence(inner) ? '' : aside);
|
|
141
|
+
const sentences = sentencesOf(unasided);
|
|
142
|
+
const kept = sentences.map(sentenceWithoutInstruction).filter(Boolean);
|
|
143
|
+
return unasided === value && kept.length === sentences.length && kept.every((sentence, index)=>sentence === sentences[index]) ? value : kept.join(' ');
|
|
144
|
+
}
|
|
145
|
+
/** Whether a value tells the owner to do something anywhere in it (AGL-4092). */ const holdsInstruction = (value)=>aiDatasetValueWithoutInstructions(value) !== value;
|
|
146
|
+
/** A name that ends in a number, with any aside after it: "Upcoming race 1 (details to be added)". */ const NUMBERED_NAME = /^(.*?)\s*(?:#|no\.?\s*)?\d+\s*(?:\([^)]*\))?$/i;
|
|
147
|
+
/** Words a numbered placeholder is made of besides the list's own: "Item 1", "Upcoming event 2", "New entry 3". */ const STAND_IN_WORDS = new Set('item entry record row event name product thing option sample example test placeholder untitled new upcoming past next previous future current recent old'.split(' '));
|
|
148
|
+
/** A name's words as a stem compares them: lowercase, a plural's "-s" dropped. */ const stemWords = (text)=>text.toLowerCase().split(RegExp("[^\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376-\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0560-\\u0588\\u05D0-\\u05EA\\u05EF-\\u05F2\\u0620-\\u064A\\u066E-\\u066F\\u0671-\\u06D3\\u06D5\\u06E5-\\u06E6\\u06EE-\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4-\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u0860-\\u086A\\u0870-\\u0887\\u0889-\\u088F\\u08A0-\\u08C9\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F-\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC-\\u09DD\\u09DF-\\u09E1\\u09F0-\\u09F1\\u09FC\\u0A05-\\u0A0A\\u0A0F-\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32-\\u0A33\\u0A35-\\u0A36\\u0A38-\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2-\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0-\\u0AE1\\u0AF9\\u0B05-\\u0B0C\\u0B0F-\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32-\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C-\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99-\\u0B9A\\u0B9C\\u0B9E-\\u0B9F\\u0BA3-\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58-\\u0C5A\\u0C5C-\\u0C5D\\u0C60-\\u0C61\\u0C80\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDC-\\u0CDE\\u0CE0-\\u0CE1\\u0CF1-\\u0CF2\\u0D04-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D54-\\u0D56\\u0D5F-\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32-\\u0E33\\u0E40-\\u0E46\\u0E81-\\u0E82\\u0E84\\u0E86-\\u0E8A\\u0E8C-\\u0EA3\\u0EA5\\u0EA7-\\u0EB0\\u0EB2-\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065-\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F5\\u13F8-\\u13FD\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16F1-\\u16F8\\u1700-\\u1711\\u171F-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1878\\u1880-\\u1884\\u1887-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4C\\u1B83-\\u1BA0\\u1BAE-\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1C80-\\u1C8A\\u1C90-\\u1CBA\\u1CBD-\\u1CBF\\u1CE9-\\u1CEC\\u1CEE-\\u1CF3\\u1CF5-\\u1CF6\\u1CFA\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183-\\u2184\\u2C00-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3006\\u3031-\\u3035\\u303B-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312F\\u3131-\\u318E\\u31A0-\\u31BF\\u31F0-\\u31FF\\u3400-\\u4DBF\\u4E00-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A-\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6E5\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA7DC\\uA7F1-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA8FD-\\uA8FE\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5-\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB69\\uAB70-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40-\\uFB41\\uFB43-\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC\\u{10000}-\\u{1000B}\\u{1000D}-\\u{10026}\\u{10028}-\\u{1003A}\\u{1003C}-\\u{1003D}\\u{1003F}-\\u{1004D}\\u{10050}-\\u{1005D}\\u{10080}-\\u{100FA}\\u{10280}-\\u{1029C}\\u{102A0}-\\u{102D0}\\u{10300}-\\u{1031F}\\u{1032D}-\\u{10340}\\u{10342}-\\u{10349}\\u{10350}-\\u{10375}\\u{10380}-\\u{1039D}\\u{103A0}-\\u{103C3}\\u{103C8}-\\u{103CF}\\u{10400}-\\u{1049D}\\u{104B0}-\\u{104D3}\\u{104D8}-\\u{104FB}\\u{10500}-\\u{10527}\\u{10530}-\\u{10563}\\u{10570}-\\u{1057A}\\u{1057C}-\\u{1058A}\\u{1058C}-\\u{10592}\\u{10594}-\\u{10595}\\u{10597}-\\u{105A1}\\u{105A3}-\\u{105B1}\\u{105B3}-\\u{105B9}\\u{105BB}-\\u{105BC}\\u{105C0}-\\u{105F3}\\u{10600}-\\u{10736}\\u{10740}-\\u{10755}\\u{10760}-\\u{10767}\\u{10780}-\\u{10785}\\u{10787}-\\u{107B0}\\u{107B2}-\\u{107BA}\\u{10800}-\\u{10805}\\u{10808}\\u{1080A}-\\u{10835}\\u{10837}-\\u{10838}\\u{1083C}\\u{1083F}-\\u{10855}\\u{10860}-\\u{10876}\\u{10880}-\\u{1089E}\\u{108E0}-\\u{108F2}\\u{108F4}-\\u{108F5}\\u{10900}-\\u{10915}\\u{10920}-\\u{10939}\\u{10940}-\\u{10959}\\u{10980}-\\u{109B7}\\u{109BE}-\\u{109BF}\\u{10A00}\\u{10A10}-\\u{10A13}\\u{10A15}-\\u{10A17}\\u{10A19}-\\u{10A35}\\u{10A60}-\\u{10A7C}\\u{10A80}-\\u{10A9C}\\u{10AC0}-\\u{10AC7}\\u{10AC9}-\\u{10AE4}\\u{10B00}-\\u{10B35}\\u{10B40}-\\u{10B55}\\u{10B60}-\\u{10B72}\\u{10B80}-\\u{10B91}\\u{10C00}-\\u{10C48}\\u{10C80}-\\u{10CB2}\\u{10CC0}-\\u{10CF2}\\u{10D00}-\\u{10D23}\\u{10D4A}-\\u{10D65}\\u{10D6F}-\\u{10D85}\\u{10E80}-\\u{10EA9}\\u{10EB0}-\\u{10EB1}\\u{10EC2}-\\u{10EC7}\\u{10F00}-\\u{10F1C}\\u{10F27}\\u{10F30}-\\u{10F45}\\u{10F70}-\\u{10F81}\\u{10FB0}-\\u{10FC4}\\u{10FE0}-\\u{10FF6}\\u{11003}-\\u{11037}\\u{11071}-\\u{11072}\\u{11075}\\u{11083}-\\u{110AF}\\u{110D0}-\\u{110E8}\\u{11103}-\\u{11126}\\u{11144}\\u{11147}\\u{11150}-\\u{11172}\\u{11176}\\u{11183}-\\u{111B2}\\u{111C1}-\\u{111C4}\\u{111DA}\\u{111DC}\\u{11200}-\\u{11211}\\u{11213}-\\u{1122B}\\u{1123F}-\\u{11240}\\u{11280}-\\u{11286}\\u{11288}\\u{1128A}-\\u{1128D}\\u{1128F}-\\u{1129D}\\u{1129F}-\\u{112A8}\\u{112B0}-\\u{112DE}\\u{11305}-\\u{1130C}\\u{1130F}-\\u{11310}\\u{11313}-\\u{11328}\\u{1132A}-\\u{11330}\\u{11332}-\\u{11333}\\u{11335}-\\u{11339}\\u{1133D}\\u{11350}\\u{1135D}-\\u{11361}\\u{11380}-\\u{11389}\\u{1138B}\\u{1138E}\\u{11390}-\\u{113B5}\\u{113B7}\\u{113D1}\\u{113D3}\\u{11400}-\\u{11434}\\u{11447}-\\u{1144A}\\u{1145F}-\\u{11461}\\u{11480}-\\u{114AF}\\u{114C4}-\\u{114C5}\\u{114C7}\\u{11580}-\\u{115AE}\\u{115D8}-\\u{115DB}\\u{11600}-\\u{1162F}\\u{11644}\\u{11680}-\\u{116AA}\\u{116B8}\\u{11700}-\\u{1171A}\\u{11740}-\\u{11746}\\u{11800}-\\u{1182B}\\u{118A0}-\\u{118DF}\\u{118FF}-\\u{11906}\\u{11909}\\u{1190C}-\\u{11913}\\u{11915}-\\u{11916}\\u{11918}-\\u{1192F}\\u{1193F}\\u{11941}\\u{119A0}-\\u{119A7}\\u{119AA}-\\u{119D0}\\u{119E1}\\u{119E3}\\u{11A00}\\u{11A0B}-\\u{11A32}\\u{11A3A}\\u{11A50}\\u{11A5C}-\\u{11A89}\\u{11A9D}\\u{11AB0}-\\u{11AF8}\\u{11BC0}-\\u{11BE0}\\u{11C00}-\\u{11C08}\\u{11C0A}-\\u{11C2E}\\u{11C40}\\u{11C72}-\\u{11C8F}\\u{11D00}-\\u{11D06}\\u{11D08}-\\u{11D09}\\u{11D0B}-\\u{11D30}\\u{11D46}\\u{11D60}-\\u{11D65}\\u{11D67}-\\u{11D68}\\u{11D6A}-\\u{11D89}\\u{11D98}\\u{11DB0}-\\u{11DDB}\\u{11EE0}-\\u{11EF2}\\u{11F02}\\u{11F04}-\\u{11F10}\\u{11F12}-\\u{11F33}\\u{11FB0}\\u{12000}-\\u{12399}\\u{12480}-\\u{12543}\\u{12F90}-\\u{12FF0}\\u{13000}-\\u{1342F}\\u{13441}-\\u{13446}\\u{13460}-\\u{143FA}\\u{14400}-\\u{14646}\\u{16100}-\\u{1611D}\\u{16800}-\\u{16A38}\\u{16A40}-\\u{16A5E}\\u{16A70}-\\u{16ABE}\\u{16AD0}-\\u{16AED}\\u{16B00}-\\u{16B2F}\\u{16B40}-\\u{16B43}\\u{16B63}-\\u{16B77}\\u{16B7D}-\\u{16B8F}\\u{16D40}-\\u{16D6C}\\u{16E40}-\\u{16E7F}\\u{16EA0}-\\u{16EB8}\\u{16EBB}-\\u{16ED3}\\u{16F00}-\\u{16F4A}\\u{16F50}\\u{16F93}-\\u{16F9F}\\u{16FE0}-\\u{16FE1}\\u{16FE3}\\u{16FF2}-\\u{16FF3}\\u{17000}-\\u{18CD5}\\u{18CFF}-\\u{18D1E}\\u{18D80}-\\u{18DF2}\\u{1AFF0}-\\u{1AFF3}\\u{1AFF5}-\\u{1AFFB}\\u{1AFFD}-\\u{1AFFE}\\u{1B000}-\\u{1B122}\\u{1B132}\\u{1B150}-\\u{1B152}\\u{1B155}\\u{1B164}-\\u{1B167}\\u{1B170}-\\u{1B2FB}\\u{1BC00}-\\u{1BC6A}\\u{1BC70}-\\u{1BC7C}\\u{1BC80}-\\u{1BC88}\\u{1BC90}-\\u{1BC99}\\u{1D400}-\\u{1D454}\\u{1D456}-\\u{1D49C}\\u{1D49E}-\\u{1D49F}\\u{1D4A2}\\u{1D4A5}-\\u{1D4A6}\\u{1D4A9}-\\u{1D4AC}\\u{1D4AE}-\\u{1D4B9}\\u{1D4BB}\\u{1D4BD}-\\u{1D4C3}\\u{1D4C5}-\\u{1D505}\\u{1D507}-\\u{1D50A}\\u{1D50D}-\\u{1D514}\\u{1D516}-\\u{1D51C}\\u{1D51E}-\\u{1D539}\\u{1D53B}-\\u{1D53E}\\u{1D540}-\\u{1D544}\\u{1D546}\\u{1D54A}-\\u{1D550}\\u{1D552}-\\u{1D6A5}\\u{1D6A8}-\\u{1D6C0}\\u{1D6C2}-\\u{1D6DA}\\u{1D6DC}-\\u{1D6FA}\\u{1D6FC}-\\u{1D714}\\u{1D716}-\\u{1D734}\\u{1D736}-\\u{1D74E}\\u{1D750}-\\u{1D76E}\\u{1D770}-\\u{1D788}\\u{1D78A}-\\u{1D7A8}\\u{1D7AA}-\\u{1D7C2}\\u{1D7C4}-\\u{1D7CB}\\u{1DF00}-\\u{1DF1E}\\u{1DF25}-\\u{1DF2A}\\u{1E030}-\\u{1E06D}\\u{1E100}-\\u{1E12C}\\u{1E137}-\\u{1E13D}\\u{1E14E}\\u{1E290}-\\u{1E2AD}\\u{1E2C0}-\\u{1E2EB}\\u{1E4D0}-\\u{1E4EB}\\u{1E5D0}-\\u{1E5ED}\\u{1E5F0}\\u{1E6C0}-\\u{1E6DE}\\u{1E6E0}-\\u{1E6E2}\\u{1E6E4}-\\u{1E6E5}\\u{1E6E7}-\\u{1E6ED}\\u{1E6F0}-\\u{1E6F4}\\u{1E6FE}-\\u{1E6FF}\\u{1E7E0}-\\u{1E7E6}\\u{1E7E8}-\\u{1E7EB}\\u{1E7ED}-\\u{1E7EE}\\u{1E7F0}-\\u{1E7FE}\\u{1E800}-\\u{1E8C4}\\u{1E900}-\\u{1E943}\\u{1E94B}\\u{1EE00}-\\u{1EE03}\\u{1EE05}-\\u{1EE1F}\\u{1EE21}-\\u{1EE22}\\u{1EE24}\\u{1EE27}\\u{1EE29}-\\u{1EE32}\\u{1EE34}-\\u{1EE37}\\u{1EE39}\\u{1EE3B}\\u{1EE42}\\u{1EE47}\\u{1EE49}\\u{1EE4B}\\u{1EE4D}-\\u{1EE4F}\\u{1EE51}-\\u{1EE52}\\u{1EE54}\\u{1EE57}\\u{1EE59}\\u{1EE5B}\\u{1EE5D}\\u{1EE5F}\\u{1EE61}-\\u{1EE62}\\u{1EE64}\\u{1EE67}-\\u{1EE6A}\\u{1EE6C}-\\u{1EE72}\\u{1EE74}-\\u{1EE77}\\u{1EE79}-\\u{1EE7C}\\u{1EE7E}\\u{1EE80}-\\u{1EE89}\\u{1EE8B}-\\u{1EE9B}\\u{1EEA1}-\\u{1EEA3}\\u{1EEA5}-\\u{1EEA9}\\u{1EEAB}-\\u{1EEBB}\\u{20000}-\\u{2A6DF}\\u{2A700}-\\u{2B81D}\\u{2B820}-\\u{2CEAD}\\u{2CEB0}-\\u{2EBE0}\\u{2EBF0}-\\u{2EE5D}\\u{2F800}-\\u{2FA1D}\\u{30000}-\\u{3134A}\\u{31350}-\\u{33479}\\u0030-\\u0039\\u00B2-\\u00B3\\u00B9\\u00BC-\\u00BE\\u0660-\\u0669\\u06F0-\\u06F9\\u07C0-\\u07C9\\u0966-\\u096F\\u09E6-\\u09EF\\u09F4-\\u09F9\\u0A66-\\u0A6F\\u0AE6-\\u0AEF\\u0B66-\\u0B6F\\u0B72-\\u0B77\\u0BE6-\\u0BF2\\u0C66-\\u0C6F\\u0C78-\\u0C7E\\u0CE6-\\u0CEF\\u0D58-\\u0D5E\\u0D66-\\u0D78\\u0DE6-\\u0DEF\\u0E50-\\u0E59\\u0ED0-\\u0ED9\\u0F20-\\u0F33\\u1040-\\u1049\\u1090-\\u1099\\u1369-\\u137C\\u16EE-\\u16F0\\u17E0-\\u17E9\\u17F0-\\u17F9\\u1810-\\u1819\\u1946-\\u194F\\u19D0-\\u19DA\\u1A80-\\u1A89\\u1A90-\\u1A99\\u1B50-\\u1B59\\u1BB0-\\u1BB9\\u1C40-\\u1C49\\u1C50-\\u1C59\\u2070\\u2074-\\u2079\\u2080-\\u2089\\u2150-\\u2182\\u2185-\\u2189\\u2460-\\u249B\\u24EA-\\u24FF\\u2776-\\u2793\\u2CFD\\u3007\\u3021-\\u3029\\u3038-\\u303A\\u3192-\\u3195\\u3220-\\u3229\\u3248-\\u324F\\u3251-\\u325F\\u3280-\\u3289\\u32B1-\\u32BF\\uA620-\\uA629\\uA6E6-\\uA6EF\\uA830-\\uA835\\uA8D0-\\uA8D9\\uA900-\\uA909\\uA9D0-\\uA9D9\\uA9F0-\\uA9F9\\uAA50-\\uAA59\\uABF0-\\uABF9\\uFF10-\\uFF19\\u{10107}-\\u{10133}\\u{10140}-\\u{10178}\\u{1018A}-\\u{1018B}\\u{102E1}-\\u{102FB}\\u{10320}-\\u{10323}\\u{10341}\\u{1034A}\\u{103D1}-\\u{103D5}\\u{104A0}-\\u{104A9}\\u{10858}-\\u{1085F}\\u{10879}-\\u{1087F}\\u{108A7}-\\u{108AF}\\u{108FB}-\\u{108FF}\\u{10916}-\\u{1091B}\\u{109BC}-\\u{109BD}\\u{109C0}-\\u{109CF}\\u{109D2}-\\u{109FF}\\u{10A40}-\\u{10A48}\\u{10A7D}-\\u{10A7E}\\u{10A9D}-\\u{10A9F}\\u{10AEB}-\\u{10AEF}\\u{10B58}-\\u{10B5F}\\u{10B78}-\\u{10B7F}\\u{10BA9}-\\u{10BAF}\\u{10CFA}-\\u{10CFF}\\u{10D30}-\\u{10D39}\\u{10D40}-\\u{10D49}\\u{10E60}-\\u{10E7E}\\u{10F1D}-\\u{10F26}\\u{10F51}-\\u{10F54}\\u{10FC5}-\\u{10FCB}\\u{11052}-\\u{1106F}\\u{110F0}-\\u{110F9}\\u{11136}-\\u{1113F}\\u{111D0}-\\u{111D9}\\u{111E1}-\\u{111F4}\\u{112F0}-\\u{112F9}\\u{11450}-\\u{11459}\\u{114D0}-\\u{114D9}\\u{11650}-\\u{11659}\\u{116C0}-\\u{116C9}\\u{116D0}-\\u{116E3}\\u{11730}-\\u{1173B}\\u{118E0}-\\u{118F2}\\u{11950}-\\u{11959}\\u{11BF0}-\\u{11BF9}\\u{11C50}-\\u{11C6C}\\u{11D50}-\\u{11D59}\\u{11DA0}-\\u{11DA9}\\u{11DE0}-\\u{11DE9}\\u{11F50}-\\u{11F59}\\u{11FC0}-\\u{11FD4}\\u{12400}-\\u{1246E}\\u{16130}-\\u{16139}\\u{16A60}-\\u{16A69}\\u{16AC0}-\\u{16AC9}\\u{16B50}-\\u{16B59}\\u{16B5B}-\\u{16B61}\\u{16D70}-\\u{16D79}\\u{16E80}-\\u{16E96}\\u{16FF4}-\\u{16FF6}\\u{1CCF0}-\\u{1CCF9}\\u{1D2C0}-\\u{1D2D3}\\u{1D2E0}-\\u{1D2F3}\\u{1D360}-\\u{1D378}\\u{1D7CE}-\\u{1D7FF}\\u{1E140}-\\u{1E149}\\u{1E2F0}-\\u{1E2F9}\\u{1E4F0}-\\u{1E4F9}\\u{1E5F1}-\\u{1E5FA}\\u{1E8C7}-\\u{1E8CF}\\u{1E950}-\\u{1E959}\\u{1EC71}-\\u{1ECAB}\\u{1ECAD}-\\u{1ECAF}\\u{1ECB1}-\\u{1ECB4}\\u{1ED01}-\\u{1ED2D}\\u{1ED2F}-\\u{1ED3D}\\u{1F100}-\\u{1F10C}\\u{1FBF0}-\\u{1FBF9}]+", "u")).filter(Boolean).map((word)=>word.length > 3 && word.endsWith('s') && !word.endsWith('ss') ? word.slice(0, -1) : word);
|
|
149
|
+
/**
|
|
150
|
+
* The records of a designed dataset that are placeholders (AGL-4065), by
|
|
151
|
+
* position, which no site shows:
|
|
152
|
+
* - any value that marks a gap: "details to be added", "TBD", "coming soon";
|
|
153
|
+
* - any sentence that tells the owner what to add instead of saying it
|
|
154
|
+
* (AGL-4092): "Add this rider's story here.", "Add finishing position";
|
|
155
|
+
* - a name that is a number after nothing but the list's own words and
|
|
156
|
+
* stand-in words: "Item 1", "Race 2", "Upcoming race 3", "New entry 4".
|
|
157
|
+
* A course's "Level 1" to "Level 3" and a series' "Series Round 1 at
|
|
158
|
+
* Lockhart" name things, and are kept. Pure.
|
|
159
|
+
*/ export function aiDatasetPlaceholderRecords(dataset, name = '') {
|
|
160
|
+
const own = new Set([
|
|
161
|
+
...stemWords(name),
|
|
162
|
+
...dataset.fields.flatMap((field)=>stemWords(field.name))
|
|
163
|
+
]);
|
|
164
|
+
return dataset.records.flatMap((values, index)=>{
|
|
165
|
+
var _values_, _match_;
|
|
166
|
+
if (values.some((value)=>PLACEHOLDER_TEXT.test(value != null ? value : '') || holdsInstruction(value != null ? value : ''))) return [
|
|
167
|
+
index
|
|
168
|
+
];
|
|
169
|
+
const match = NUMBERED_NAME.exec(((_values_ = values[0]) != null ? _values_ : '').replace(/\s+/g, ' ').trim());
|
|
170
|
+
if (!match) return [];
|
|
171
|
+
const words = stemWords((_match_ = match[1]) != null ? _match_ : '');
|
|
172
|
+
return words.every((word)=>own.has(word) || STAND_IN_WORDS.has(word)) ? [
|
|
173
|
+
index
|
|
174
|
+
] : [];
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/** An answer's fields and records as the placeholder test reads them, whatever else is wrong with it. */ function answerShape(answer) {
|
|
178
|
+
const text = (value)=>typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
|
|
179
|
+
const fields = (Array.isArray(answer == null ? void 0 : answer['fields']) ? answer == null ? void 0 : answer['fields'] : []).map((field)=>({
|
|
180
|
+
name: text(field == null ? void 0 : field['name'])
|
|
181
|
+
}));
|
|
182
|
+
const records = (Array.isArray(answer == null ? void 0 : answer['records']) ? answer == null ? void 0 : answer['records'] : []).map((record)=>{
|
|
183
|
+
const values = record == null ? void 0 : record['values'];
|
|
184
|
+
return Array.isArray(values) ? values.map(text) : [];
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
fields,
|
|
188
|
+
records
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* A dataset's answer held to the tool's check and to naming real things
|
|
193
|
+
* (AGL-4065): one with placeholder records is refused, with
|
|
194
|
+
* {@link AI_DATASET_PLACEHOLDER_REASK} for the one re-ask. Pure.
|
|
195
|
+
*/ export function checkAiDatasetRecords(answer, context) {
|
|
196
|
+
var _checked_offending;
|
|
197
|
+
const checked = checkAiDataset(answer, context);
|
|
198
|
+
const shape = answerShape(answer);
|
|
199
|
+
const placeholders = aiDatasetPlaceholderRecords(shape, context.name);
|
|
200
|
+
// A sample is of its kind, never of a make the brief does not name (AGL-4065).
|
|
201
|
+
const rawRecords = Array.isArray(answer['records']) ? answer['records'] : [];
|
|
202
|
+
const branded = shape.records.flatMap((values, index)=>{
|
|
203
|
+
var _rawRecords_index;
|
|
204
|
+
const sample = ((_rawRecords_index = rawRecords[index]) == null ? void 0 : _rawRecords_index['sample']) === true;
|
|
205
|
+
const brand = sample ? values.map((value)=>aiTextNamesBrand(value, context.merchantWords)).find(Boolean) : null;
|
|
206
|
+
return brand ? [
|
|
207
|
+
{
|
|
208
|
+
index,
|
|
209
|
+
brand
|
|
210
|
+
}
|
|
211
|
+
] : [];
|
|
212
|
+
});
|
|
213
|
+
if (!placeholders.length && !branded.length) return checked;
|
|
214
|
+
const violations = [
|
|
215
|
+
...checked.violations
|
|
216
|
+
];
|
|
217
|
+
const offending = _extends({}, (_checked_offending = checked.offending) != null ? _checked_offending : {});
|
|
218
|
+
if (placeholders.length) {
|
|
219
|
+
var _ref;
|
|
220
|
+
var _shape_records_index;
|
|
221
|
+
violations.push({
|
|
222
|
+
rule: null,
|
|
223
|
+
code: AI_DATASET_PLACEHOLDER_CODE,
|
|
224
|
+
message: AI_DATASET_PLACEHOLDER_REASK,
|
|
225
|
+
paths: placeholders.map((index)=>`records[${index}]`)
|
|
226
|
+
});
|
|
227
|
+
for (const index of placeholders.slice(0, 3))offending[`records[${index}]`] = (_ref = (_shape_records_index = shape.records[index]) == null ? void 0 : _shape_records_index[0]) != null ? _ref : '';
|
|
228
|
+
}
|
|
229
|
+
for (const { index, brand } of branded){
|
|
230
|
+
violations.push({
|
|
231
|
+
rule: null,
|
|
232
|
+
code: 'sample-brand',
|
|
233
|
+
message: `Sample record ${index + 1} names the make "${brand}", which the brief does not: a sample is of its kind, never of a brand.`,
|
|
234
|
+
paths: [
|
|
235
|
+
`records[${index}]`
|
|
236
|
+
]
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
value: null,
|
|
241
|
+
violations,
|
|
242
|
+
offending
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* A dataset answered with placeholders after its one re-ask, kept with the
|
|
247
|
+
* records that name real things (AGL-4065) — however few, none included —
|
|
248
|
+
* where nothing else in it breaks a rule; `null` otherwise. Pure.
|
|
249
|
+
*/ export function aiDatasetWithoutPlaceholders(answer, context) {
|
|
250
|
+
if (!answer) return null;
|
|
251
|
+
const drop = new Set(aiDatasetPlaceholderRecords(answerShape(answer), context.name));
|
|
252
|
+
const records = Array.isArray(answer['records']) ? answer['records'] : [];
|
|
253
|
+
const kept = _extends({}, answer, {
|
|
254
|
+
records: records.filter((_record, index)=>!drop.has(index))
|
|
255
|
+
});
|
|
256
|
+
// The fewest records a list is asked for is for one the brief fills; what is left of this one is what it gave.
|
|
257
|
+
const checked = checkAiDataset(kept, _extends({}, context, {
|
|
258
|
+
recordsMin: 1
|
|
259
|
+
}));
|
|
260
|
+
return checked.value && !checked.violations.length ? checked.value : null;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* An answer with every instruction to the owner taken out of its records'
|
|
264
|
+
* values (AGL-4092), for the salvage after its one re-ask: "Add this rider's
|
|
265
|
+
* story here." leaves the bio's other sentences, "Add finishing position"
|
|
266
|
+
* leaves the result "", and a record with no name left is dropped. The
|
|
267
|
+
* answer unchanged where no value instructed. Pure.
|
|
268
|
+
*/ export function aiDatasetWithoutInstructions(answer) {
|
|
269
|
+
if (!answer || !Array.isArray(answer['records'])) return answer;
|
|
270
|
+
let changed = false;
|
|
271
|
+
const records = answer['records'].flatMap((record)=>{
|
|
272
|
+
const values = record == null ? void 0 : record['values'];
|
|
273
|
+
if (!Array.isArray(values)) return [
|
|
274
|
+
record
|
|
275
|
+
];
|
|
276
|
+
const next = values.map((value)=>typeof value === 'string' ? aiDatasetValueWithoutInstructions(value) : value);
|
|
277
|
+
if (next.every((value, index)=>value === values[index])) return [
|
|
278
|
+
record
|
|
279
|
+
];
|
|
280
|
+
changed = true;
|
|
281
|
+
return typeof next[0] === 'string' && next[0].trim() ? [
|
|
282
|
+
_extends({}, record, {
|
|
283
|
+
values: next
|
|
284
|
+
})
|
|
285
|
+
] : [];
|
|
286
|
+
});
|
|
287
|
+
return changed ? _extends({}, answer, {
|
|
288
|
+
records
|
|
289
|
+
}) : answer;
|
|
290
|
+
}
|
|
291
|
+
/** A designed dataset with every instruction to the owner taken out (AGL-4092), as {@link aiDatasetWithoutInstructions} does an answer. */ function designedWithoutInstructions(dataset) {
|
|
292
|
+
const records = dataset.records.map((values)=>values.map((value)=>aiDatasetValueWithoutInstructions(value != null ? value : '')));
|
|
293
|
+
if (records.every((values, index)=>values.every((value, column)=>{
|
|
294
|
+
var _ref;
|
|
295
|
+
var _dataset_records_index;
|
|
296
|
+
return value === ((_ref = (_dataset_records_index = dataset.records[index]) == null ? void 0 : _dataset_records_index[column]) != null ? _ref : '');
|
|
297
|
+
}))) return dataset;
|
|
298
|
+
const keep = records.map((values)=>{
|
|
299
|
+
var _values_;
|
|
300
|
+
return Boolean((_values_ = values[0]) == null ? void 0 : _values_.trim());
|
|
301
|
+
});
|
|
302
|
+
const { samples: all } = dataset, rest = _object_without_properties_loose(dataset, [
|
|
303
|
+
"samples"
|
|
304
|
+
]);
|
|
305
|
+
const samples = all == null ? void 0 : all.filter((_sample, index)=>keep[index]);
|
|
306
|
+
return _extends({}, rest, {
|
|
307
|
+
records: records.filter((_values, index)=>keep[index])
|
|
308
|
+
}, (samples == null ? void 0 : samples.some(Boolean)) ? {
|
|
309
|
+
samples
|
|
310
|
+
} : {});
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* A designed dataset with any instruction to the owner (AGL-4092) and any
|
|
314
|
+
* placeholder record taken out (AGL-4065), and where the list starts with no
|
|
315
|
+
* samples (`AI_DATASET_LIST_WITHOUT_ENTRIES` is `empty`), every sample too.
|
|
316
|
+
* Pure.
|
|
317
|
+
*/ export function aiDatasetRealRecords(designed, name) {
|
|
318
|
+
var _dataset_samples;
|
|
319
|
+
const dataset = designedWithoutInstructions(designed);
|
|
320
|
+
const drop = new Set(aiDatasetPlaceholderRecords(dataset, name));
|
|
321
|
+
if (AI_DATASET_LIST_WITHOUT_ENTRIES === 'empty') (_dataset_samples = dataset.samples) == null ? void 0 : _dataset_samples.forEach((sample, index)=>sample && drop.add(index));
|
|
322
|
+
if (!drop.size) return dataset;
|
|
323
|
+
const keep = (_entry, index)=>!drop.has(index);
|
|
324
|
+
const { samples: all } = dataset, rest = _object_without_properties_loose(dataset, [
|
|
325
|
+
"samples"
|
|
326
|
+
]);
|
|
327
|
+
const samples = all == null ? void 0 : all.filter(keep);
|
|
328
|
+
return _extends({}, rest, {
|
|
329
|
+
records: dataset.records.filter(keep)
|
|
330
|
+
}, (samples == null ? void 0 : samples.some(Boolean)) ? {
|
|
331
|
+
samples
|
|
332
|
+
} : {});
|
|
333
|
+
}
|
|
334
|
+
// ── A sample row's date (AGL-4065) ──────────────────────────────────────
|
|
335
|
+
/** A field whose name says it holds when a record happens. */ const DATE_FIELD = /\b(?:date|dates|day|when)\b/i;
|
|
336
|
+
/** A field a record has only once it has happened: a race's result, a match's score. */ const OUTCOME_FIELD = /\b(?:results?|finish(?:ed)?|placings?|placed|winners?|scores?|outcome)\b/i;
|
|
337
|
+
/** A value or a section's name that says its records are behind us. */ const PAST_WORDS = /\b(?:past|previous|completed|finished|results?|earlier|archived?|last season)\b/i;
|
|
338
|
+
/** The days a sample's date steps by, and how far from the job's day the first falls: a season of weekends. */ export const AI_DATASET_SAMPLE_DATES = {
|
|
339
|
+
stepDays: 21,
|
|
340
|
+
leadDays: 14
|
|
341
|
+
};
|
|
342
|
+
const DAY_MS = 86400000;
|
|
343
|
+
/** The Saturday on or after (`ahead`) or on or before a day, at noon UTC. */ function saturday(from, ahead) {
|
|
344
|
+
const day = new Date(Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), from.getUTCDate(), 12));
|
|
345
|
+
const shift = ahead ? (6 - day.getUTCDay() + 7) % 7 : -((day.getUTCDay() + 1) % 7);
|
|
346
|
+
return new Date(day.getTime() + shift * DAY_MS);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* A dated list's SAMPLE records given real dates (AGL-4065): a sample the
|
|
350
|
+
* brief gave no date for falls on a weekend of a season from the job's day —
|
|
351
|
+
* the first Saturday at least two weeks out, then every three weeks — and a
|
|
352
|
+
* sample that is behind us (its split value or the list says past: "Past
|
|
353
|
+
* results") on the Saturdays as far back, the latest first, with no result
|
|
354
|
+
* left on one still to come. Written as a visitor reads a date, in the
|
|
355
|
+
* site's language. A record from the brief keeps its own date. Pure.
|
|
356
|
+
*/ export function aiDatasetSampleDates(dataset, options) {
|
|
357
|
+
var _dataset_samples;
|
|
358
|
+
const samples = (_dataset_samples = dataset.samples) != null ? _dataset_samples : [];
|
|
359
|
+
const dates = dataset.fields.flatMap((field, column)=>column > 0 && field.type === 'text' && DATE_FIELD.test(field.name) ? [
|
|
360
|
+
column
|
|
361
|
+
] : []);
|
|
362
|
+
if (!samples.some(Boolean) || !dates.length) return dataset;
|
|
363
|
+
const splitColumn = options.split ? dataset.fields.findIndex((field)=>field.name.trim().toLowerCase() === options.split.field.trim().toLowerCase()) : -1;
|
|
364
|
+
const outcomes = dataset.fields.flatMap((field, column)=>column > 0 && OUTCOME_FIELD.test(field.name) ? [
|
|
365
|
+
column
|
|
366
|
+
] : []);
|
|
367
|
+
let format;
|
|
368
|
+
try {
|
|
369
|
+
format = new Intl.DateTimeFormat(options.language || 'en-US', {
|
|
370
|
+
weekday: 'short',
|
|
371
|
+
month: 'short',
|
|
372
|
+
day: 'numeric',
|
|
373
|
+
year: 'numeric',
|
|
374
|
+
timeZone: 'UTC'
|
|
375
|
+
});
|
|
376
|
+
} catch (unused) {
|
|
377
|
+
format = new Intl.DateTimeFormat('en-US', {
|
|
378
|
+
weekday: 'short',
|
|
379
|
+
month: 'short',
|
|
380
|
+
day: 'numeric',
|
|
381
|
+
year: 'numeric',
|
|
382
|
+
timeZone: 'UTC'
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
const { stepDays, leadDays } = AI_DATASET_SAMPLE_DATES;
|
|
386
|
+
const firstAhead = saturday(new Date(options.now.getTime() + leadDays * DAY_MS), true);
|
|
387
|
+
const firstBehind = saturday(new Date(options.now.getTime() - leadDays * DAY_MS), false);
|
|
388
|
+
let ahead = 0;
|
|
389
|
+
let behind = 0;
|
|
390
|
+
return _extends({}, dataset, {
|
|
391
|
+
records: dataset.records.map((values, index)=>{
|
|
392
|
+
var _values_splitColumn;
|
|
393
|
+
if (!samples[index]) return [
|
|
394
|
+
...values
|
|
395
|
+
];
|
|
396
|
+
const past = options.pastOnly || splitColumn > 0 && PAST_WORDS.test((_values_splitColumn = values[splitColumn]) != null ? _values_splitColumn : '');
|
|
397
|
+
const at = past ? new Date(firstBehind.getTime() - behind++ * stepDays * DAY_MS) : new Date(firstAhead.getTime() + ahead++ * stepDays * DAY_MS);
|
|
398
|
+
const said = format.format(at);
|
|
399
|
+
// A race still to come has no result, and one behind us never an instruction for one (AGL-4092).
|
|
400
|
+
return values.map((value, column)=>dates.includes(column) ? said : outcomes.includes(column) && (!past || holdsInstruction(value != null ? value : '')) ? '' : value);
|
|
401
|
+
})
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
/** Whether every section that lists a dataset says its records are behind us ("Past results", "Race results"). */ export function aiDatasetListedAsPast(sections) {
|
|
405
|
+
return sections.length > 0 && sections.every((section)=>PAST_WORDS.test(section));
|
|
406
|
+
}
|
|
65
407
|
/** Design one dataset. It writes nothing to the site. */ export function generateAiDataset(input) {
|
|
66
408
|
var _input_maxTokens;
|
|
67
409
|
const row = AI_ROUTING_TABLE[AI_DATASET_STEP];
|
|
@@ -90,9 +432,10 @@ import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
|
90
432
|
} : {}, input.provider ? {
|
|
91
433
|
provider: input.provider
|
|
92
434
|
} : {}, {
|
|
93
|
-
check: (answer)=>
|
|
435
|
+
check: (answer)=>checkAiDatasetRecords(answer, {
|
|
94
436
|
planned: input.fields,
|
|
95
|
-
merchantWords: input.merchantWords
|
|
437
|
+
merchantWords: input.merchantWords,
|
|
438
|
+
name: input.name
|
|
96
439
|
})
|
|
97
440
|
}));
|
|
98
441
|
}
|
|
@@ -103,6 +446,7 @@ import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
|
103
446
|
* packages said "A easygoing session" in their records and on every page
|
|
104
447
|
* that listed them.
|
|
105
448
|
*/ export function aiDatasetDraftContent(name, dataset, options) {
|
|
449
|
+
var _dataset_samples;
|
|
106
450
|
const said = (value)=>aiLayoutArticles(value, options.language);
|
|
107
451
|
const records = dataset.records.map((values)=>{
|
|
108
452
|
const record = {};
|
|
@@ -129,7 +473,12 @@ import { aiLayoutArticles } from "../layout-language/ai-layout-copy.js";
|
|
|
129
473
|
type: field.type
|
|
130
474
|
})),
|
|
131
475
|
records
|
|
132
|
-
},
|
|
476
|
+
}, ((_dataset_samples = dataset.samples) == null ? void 0 : _dataset_samples.some(Boolean)) ? {
|
|
477
|
+
samples: dataset.records.map((_values, index)=>{
|
|
478
|
+
var _dataset_samples;
|
|
479
|
+
return ((_dataset_samples = dataset.samples) == null ? void 0 : _dataset_samples[index]) === true;
|
|
480
|
+
})
|
|
481
|
+
} : {}, options.recordPages && dataset.fields[0] ? {
|
|
133
482
|
pageAddressFrom: dataset.fields[0].name
|
|
134
483
|
} : {});
|
|
135
484
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-dataset-generation.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiStepKind } from '../providers/catalog'\nimport type { AiProvider } from '../providers/contract'\nimport { AI_ROUTING_TABLE, type AiPluginSettings } from '../providers/routing'\nimport {\n AI_DATASET_LIMITS,\n AI_DATASET_LIST_SEPARATOR,\n AI_DATASET_TOOL,\n AI_DATASET_TOOL_NAME,\n checkAiDataset,\n type AiDataset,\n} from '../tools/ai-dataset-tool'\nimport { runValidatedGeneration, type AiValidatedGeneration } from './ai-doctrine'\nimport { aiLayoutArticles } from '../layout-language/ai-layout-copy'\nimport type { AiSystemBlock } from './ai-runtime'\n\n/**\n * A SITE'S DATASETS (AGL-3616): the typed fields and first records of one\n * dataset a site plan created, through the doctrine's generation call and the\n * strict `submit_dataset` tool. The data plugin's writer stores what it\n * answers; nothing here writes.\n *\n * Routed as `job.products`, the row a store's first products are proposed on\n * (no thinking, a structured list from a brief, held to the storefront claim\n * rules): one dataset of a dozen records is the same work. Its answer is held\n * to a ceiling of its own under that row's. The rules are cached and\n * byte-identical; the site's words, the dataset and the pages that show it\n * ride in the user turn.\n */\n\n/** The step a dataset is routed as. */\nexport const AI_DATASET_STEP: AiStepKind = 'job.products'\n\n/**\n * One dataset's answer ceiling: twelve fields and twelve records of a few\n * sentences each, written as JSON, about 9,000 characters at three a token\n * with room; under the routing row's.\n */\nexport const AI_DATASET_MAX_TOKENS = Math.min(4_000, AI_ROUTING_TABLE[AI_DATASET_STEP].maxTokens)\n\n/** The generation kind a dataset is asked under, which the doctrine scopes. */\nexport const AI_DATASET_KIND = 'dataset'\n\n/** The rules for one dataset. Byte-identical on every request. */\nexport const AI_DATASET_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You design one dataset of a new website: a list of like things — a menu, a team, services, portfolio pieces, events, questions and answers — kept as records the owner edits in one place, which the site’s pages list.\\n\\n' +\n `Answer by calling ${AI_DATASET_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n [\n 'Keep the planned fields, in order and named exactly as planned; the pages around the dataset already name them. Add a field only where the records clearly need it.',\n 'Type each field: text for words, including a date or a time written the way a visitor reads it; number or integer only for a figure a visitor compares; boolean for yes or no; list for a few short tags. The first field names each record and is text.',\n `Write ${AI_DATASET_LIMITS.recordsMin} to ${AI_DATASET_LIMITS.recordsMax} records, as many as the brief supports: what the business actually offers, in its own words where the brief gives them, otherwise plain and general enough to be true of it.`,\n 'Never invent a fact: no price, date, address, phone number, award, credential, client or number the brief does not give, and no review, testimonial, rating or quote from anyone. Leave a value \"\" rather than make one up.',\n 'Never give a person a name the brief does not give: a team member the brief does not name is their role, such as \"Head chef\".',\n 'Never say anything cures, treats, heals or prevents anything, and never promise a health, financial or legal result.',\n `Values are plain text with no links, markup, emoji or square brackets, at most ${AI_DATASET_LIMITS.valueMax} characters each; a description is one to three sentences.`,\n 'Write in the language of the brief.',\n ]\n .map((rule) => `- ${rule}`)\n .join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\nexport interface AiDatasetPromptInput {\n /** The site's words: the brief and its answers, as a unit's brief carries them. */\n brief: string\n /** The dataset as the plan named it, and why it has one. */\n name: string\n why: string\n /** The field names the plan gave it. */\n fields: readonly string[]\n /** The sections that list it, by page: \"Menu › Our dishes (12 items)\". */\n shownIn: readonly string[]\n /** Whether each record also gets a page of its own. */\n recordPages: boolean\n}\n\n/** The user turn: the site's words, the dataset, and where it is shown. */\nexport function aiDatasetPrompt(input: AiDatasetPromptInput): string {\n return [\n 'Brief:',\n input.brief.trim(),\n `Dataset: “${input.name}” — ${input.why.trim() || 'the site’s structured content'}`,\n `Planned fields: ${input.fields.length ? input.fields.map((field) => `“${field}”`).join(', ') : '(none: choose them)'}`,\n input.shownIn.length ? `Listed on: ${input.shownIn.join('; ')}.` : 'Listed on the site’s pages.',\n input.recordPages\n ? 'Each record also gets a page of its own, so give each one enough to read: a sentence or two describing it.'\n : 'Each record is shown as a card, so keep its values short.',\n ].join('\\n')\n}\n\nexport interface GenerateAiDatasetInput extends AiDatasetPromptInput {\n /** What a claim or a price may be quoted from: the brief and the site's answers. */\n merchantWords: string\n settings?: AiPluginSettings\n model?: string\n maxTokens?: number\n signal?: AbortSignal\n provider?: AiProvider\n}\n\n/** Design one dataset. It writes nothing to the site. */\nexport function generateAiDataset(input: GenerateAiDatasetInput): Promise<AiValidatedGeneration<AiDataset>> {\n const row = AI_ROUTING_TABLE[AI_DATASET_STEP]\n return runValidatedGeneration(AI_DATASET_KIND, {\n step: AI_DATASET_STEP,\n ...(input.settings ? { settings: input.settings } : {}),\n ...(input.model ? { model: input.model } : {}),\n instructions: AI_DATASET_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiDatasetPrompt(input) }],\n tool: AI_DATASET_TOOL,\n maxTokens: input.maxTokens ?? AI_DATASET_MAX_TOKENS,\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n ...(input.signal ? { signal: input.signal } : {}),\n ...(input.provider ? { provider: input.provider } : {}),\n check: (answer) => checkAiDataset(answer, { planned: input.fields, merchantWords: input.merchantWords }),\n }) as Promise<AiValidatedGeneration<AiDataset>>\n}\n\n/**\n * A designed dataset as the data plugin's `dataset` writer takes it: values by\n * field name. Its words' \"a\" and \"an\" agree with what follows them in English\n * (`aiLayoutArticles`, AGL-3988), as a page's do: the beta.270 Sugar Hollow\n * packages said \"A easygoing session\" in their records and on every page\n * that listed them.\n */\nexport function aiDatasetDraftContent(\n name: string,\n dataset: AiDataset,\n options: { recordPages: boolean; language?: string | null },\n): Record<string, unknown> {\n const said = (value: string) => aiLayoutArticles(value, options.language)\n const records = dataset.records.map((values) => {\n const record: Record<string, unknown> = {}\n dataset.fields.forEach((field, column) => {\n const value = (values[column] ?? '').trim()\n if (!value) return\n if (field.type === 'list') {\n record[field.name] = value.split(AI_DATASET_LIST_SEPARATOR.trim()).map((part) => said(part.trim())).filter(Boolean)\n } else if (field.type === 'number' || field.type === 'integer') {\n record[field.name] = value.replace(/,/g, '')\n } else if (field.type === 'text') {\n record[field.name] = said(value)\n } else {\n record[field.name] = value\n }\n })\n return record\n })\n return {\n name,\n fields: dataset.fields.map((field) => ({ name: field.name, type: field.type })),\n records,\n // A record page shows each record at an address made from its name.\n ...(options.recordPages && dataset.fields[0] ? { pageAddressFrom: dataset.fields[0].name } : {}),\n }\n}\n"],"names":["AI_ROUTING_TABLE","AI_DATASET_LIMITS","AI_DATASET_LIST_SEPARATOR","AI_DATASET_TOOL","AI_DATASET_TOOL_NAME","checkAiDataset","runValidatedGeneration","aiLayoutArticles","AI_DATASET_STEP","AI_DATASET_MAX_TOKENS","Math","min","maxTokens","AI_DATASET_KIND","AI_DATASET_INSTRUCTIONS","text","recordsMin","recordsMax","valueMax","map","rule","join","cacheBreakpoint","aiDatasetPrompt","input","brief","trim","name","why","fields","length","field","shownIn","recordPages","generateAiDataset","row","step","settings","model","instructions","messages","role","content","tool","thinking","effort","signal","provider","check","answer","planned","merchantWords","aiDatasetDraftContent","dataset","options","said","value","language","records","values","record","forEach","column","type","split","part","filter","Boolean","replace","pageAddressFrom"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/ai/src/lib/runtime/ai-dataset-generation.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { AiStepKind } from '../providers/catalog'\nimport type { AiProvider } from '../providers/contract'\nimport { AI_ROUTING_TABLE, type AiPluginSettings } from '../providers/routing'\nimport {\n AI_DATASET_LIMITS,\n AI_DATASET_LIST_SEPARATOR,\n AI_DATASET_TOOL,\n AI_DATASET_TOOL_NAME,\n checkAiDataset,\n type AiDataset,\n} from '../tools/ai-dataset-tool'\nimport { runValidatedGeneration, type AiGenerationCheckResult, type AiValidatedGeneration } from './ai-doctrine'\nimport type { AiDoctrineViolation } from './ai-doctrine-validators'\nimport { aiLayoutArticles } from '../layout-language/ai-layout-copy'\nimport { aiTextNamesBrand } from '../model/ai-products'\nimport type { AiSystemBlock } from './ai-runtime'\n\n/**\n * A SITE'S DATASETS (AGL-3616): the typed fields and first records of one\n * dataset a site plan created, through the doctrine's generation call and the\n * strict `submit_dataset` tool. The data plugin's writer stores what it\n * answers; nothing here writes.\n *\n * Routed as `job.products`, the row a store's first products are proposed on\n * (no thinking, a structured list from a brief, held to the storefront claim\n * rules): one dataset of a dozen records is the same work. Its answer is held\n * to a ceiling of its own under that row's. The rules are cached and\n * byte-identical; the site's words, the dataset and the pages that show it\n * ride in the user turn.\n */\n\n/** The step a dataset is routed as. */\nexport const AI_DATASET_STEP: AiStepKind = 'job.products'\n\n/**\n * One dataset's answer ceiling: twelve fields and twelve records of a few\n * sentences each, written as JSON, about 9,000 characters at three a token\n * with room; under the routing row's.\n */\nexport const AI_DATASET_MAX_TOKENS = Math.min(4_000, AI_ROUTING_TABLE[AI_DATASET_STEP].maxTokens)\n\n/** The generation kind a dataset is asked under, which the doctrine scopes. */\nexport const AI_DATASET_KIND = 'dataset'\n\n/**\n * THE Q2 DECISION (AGL-4065): what a list the brief gives no entries for starts\n * with. `samples` (decided 2026-10-28): complete-looking SAMPLE records the\n * owner replaces, each marked a sample in Data, a dated list's dates set by\n * the platform (`aiDatasetSampleDates`), so the site looks complete and no\n * row is a placeholder. `empty`: the fields and no records, and the pages\n * say the entries are on the way. The rules, the tool's answer and the\n * pages' brief all follow this one switch.\n */\nexport const AI_DATASET_LIST_WITHOUT_ENTRIES: 'samples' | 'empty' = 'samples'\n\n/** What a list the brief gives no entries for is told to hold (AGL-4065). */\nconst WITHOUT_ENTRIES_RULE =\n AI_DATASET_LIST_WITHOUT_ENTRIES === 'samples'\n ? 'Where the brief gives no entries for the list, write sample records the owner replaces, each marked sample: a complete entry of its kind, named in the brief’s own words (such as \"Series Round 1 at Mill Creek\" for a race team that rides at Mill Creek), its other values typical of it. This is the one place a record may give a value the brief does not, and a sample still gives no price, phone number, email, address, real person’s name or brand. Leave a date \"\" in a sample, for the platform to set, and name no month or date in it. ' +\n // AGL-4092: six sample riders' bios ended \"Add this rider's story here.\", and six past races' results were \"Add finishing position\".\n 'A sample’s words read as finished copy about it: never an instruction to the owner, such as \"Add this rider\\'s story here\" or \"Add finishing position\", and never a note that it is a sample, which the console says.'\n : 'Where the brief gives no entries for the list, give the fields and no records: an empty list is an answer, a numbered placeholder is not.'\n\n/** The rules for one dataset. Byte-identical on every request. */\nexport const AI_DATASET_INSTRUCTIONS: AiSystemBlock[] = [\n {\n text:\n 'You design one dataset of a new website: a list of like things — a menu, a team, services, portfolio pieces, events, questions and answers — kept as records the owner edits in one place, which the site’s pages list.\\n\\n' +\n `Answer by calling ${AI_DATASET_TOOL_NAME} exactly once. A reply in prose cannot be used.\\n\\n` +\n 'Rules:\\n' +\n [\n 'Keep the planned fields, in order and named exactly as planned; the pages around the dataset already name them. Add a field only where the records clearly need it.',\n 'Type each field: text for words, including a date or a time written the way a visitor reads it; number or integer only for a figure a visitor compares; boolean for yes or no; list for a few short tags. The first field names each record and is text.',\n `Write ${AI_DATASET_LIMITS.recordsMin} to ${AI_DATASET_LIMITS.recordsMax} records, as many as the brief supports: what the business actually offers, in its own words where the brief gives them, otherwise plain and general enough to be true of it.`,\n 'Never invent a fact: no price, date, address, phone number, award, credential, client or number the brief does not give, and no review, testimonial, rating or quote from anyone. Leave a value \"\" rather than make one up.',\n // AGL-4065: a race schedule with no dates came back as ten numbered placeholders.\n WITHOUT_ENTRIES_RULE,\n 'Never give a person a name the brief does not give: a team member the brief does not name is their role, such as \"Head chef\".',\n 'Never say anything cures, treats, heals or prevents anything, and never promise a health, financial or legal result.',\n `Values are plain text with no links, markup, emoji or square brackets, at most ${AI_DATASET_LIMITS.valueMax} characters each; a description is one to three sentences.`,\n 'Write in the language of the brief.',\n ]\n .map((rule) => `- ${rule}`)\n .join('\\n'),\n cacheBreakpoint: true,\n },\n]\n\nexport interface AiDatasetPromptInput {\n /** The site's words: the brief and its answers, as a unit's brief carries them. */\n brief: string\n /** The dataset as the plan named it, and why it has one. */\n name: string\n why: string\n /** The field names the plan gave it. */\n fields: readonly string[]\n /** The sections that list it, by page: \"Menu › Our dishes (12 items)\". */\n shownIn: readonly string[]\n /** Whether each record also gets a page of its own. */\n recordPages: boolean\n /**\n * The sections of one page that each list part of it (AGL-4065), and the\n * value of its planned field each lists: \"Upcoming races\" the records whose\n * Status is \"Upcoming\", \"Past results\" those whose Status is \"Past\".\n */\n split?: AiDatasetSplit\n}\n\n/** A page's lists of one dataset, each the records of one value of one field (AGL-4065). */\nexport interface AiDatasetSplit {\n /** The planned field, by name: \"Status\". */\n field: string\n /** The sections, in page order. */\n sections: string[]\n /** The value each section lists, by position. */\n values: string[]\n}\n\n/** Names as a sentence lists them: “A”, “B” and “C”. */\nfunction listed(names: readonly string[], last: 'and' | 'or'): string {\n const quoted = names.map((name) => `“${name}”`)\n return quoted.length > 1 ? `${quoted.slice(0, -1).join(', ')} ${last} ${quoted[quoted.length - 1]}` : (quoted[0] ?? '')\n}\n\n/** What a dataset is told of the sections that each list part of it (AGL-4065). */\nexport function aiDatasetSplitLine(split: AiDatasetSplit): string {\n return `Sections ${listed(split.sections, 'and')} list this dataset: its “${split.field}” field takes exactly ${listed(split.values, 'or')}.`\n}\n\n/** The user turn: the site's words, the dataset, and where it is shown. */\nexport function aiDatasetPrompt(input: AiDatasetPromptInput): string {\n return [\n 'Brief:',\n input.brief.trim(),\n `Dataset: “${input.name}” — ${input.why.trim() || 'the site’s structured content'}`,\n `Planned fields: ${input.fields.length ? input.fields.map((field) => `“${field}”`).join(', ') : '(none: choose them)'}`,\n input.shownIn.length ? `Listed on: ${input.shownIn.join('; ')}.` : 'Listed on the site’s pages.',\n ...(input.split ? [aiDatasetSplitLine(input.split)] : []),\n input.recordPages\n ? 'Each record also gets a page of its own, so give each one enough to read: a sentence or two describing it.'\n : 'Each record is shown as a card, so keep its values short.',\n ].join('\\n')\n}\n\n// ── No placeholder rows (AGL-4065) ─────────────────────────────────────\n\n/**\n * The re-ask a dataset whose records are placeholders is answered with\n * (AGL-4065): the 2026-10-25 Rut Runners Racing start's \"Race events\" came\n * back as \"Upcoming race 1 (details to be added)\" to \"Past race 5 (details to\n * be added)\", because the rules forbid inventing a date and the first field\n * may not be empty. And (AGL-4092) the 2026-10-28 start's sample riders each\n * ended \"Add this rider's story here.\": an instruction to the owner is a\n * placeholder too.\n */\nexport const AI_DATASET_PLACEHOLDER_REASK =\n AI_DATASET_LIST_WITHOUT_ENTRIES === 'samples'\n ? \"Every record names a real thing the brief gives, or is a sample marked sample: a complete entry of its kind in the brief's own words. Never a placeholder such as 'Upcoming race 1 (details to be added)', 'TBD', 'coming soon' or 'Item 1'. \" +\n // AGL-4092: the samples' bios and results told the owner what to add.\n \"Never an instruction to the owner either, such as 'Add this rider's story here' or 'Add finishing position': a sample's words read as finished copy about the sample.\"\n : \"Every record names a real thing the brief gives. Where the brief gives no entries for this list — no dates, no names, no results — answer with the fields and ZERO records; never number placeholders like 'Upcoming race 1 (details to be added)'.\"\n\n/** The code a dataset's placeholder records are refused under. */\nexport const AI_DATASET_PLACEHOLDER_CODE = 'placeholder-records'\n\n/** A value that marks a gap for later rather than saying anything: in a name, a date or anywhere else. */\nconst PLACEHOLDER_TEXT =\n /\\b(?:details?|dates?|info|information|names?)\\s+(?:(?:to be|will be)\\s+)?(?:added|confirmed|announced|determined|decided|filled)\\b|\\bto be (?:added|confirmed|announced|determined|decided)\\b|\\b(?:tbd|tba|t\\.b\\.[ad])(?![a-z])|\\bcoming soon\\b|\\bplaceholder\\b|\\blorem\\b|\\b(?:sample|example|test|dummy) (?:record|entry|row|item|name)s?\\b/i\n\n/** What a sentence tells the owner to do to the record, in place of saying anything: add, write, replace. */\nconst OWNER_VERB = '(?:add|insert|write|enter|put|type|paste|replace|swap|fill in|fill out|update|describe|upload|attach)'\n\n/** How an instruction opens: its own words, imperative, first. */\nconst INSTRUCTION_START = '^\\\\s*(?:please\\\\s+)?'\n\n/**\n * Words that tell the owner to do something, where the record should say\n * something (AGL-4092): \"Add this rider's story here.\", \"Replace with your\n * own photo.\", \"Update the results later.\" Only an imperative is one: \"Riders\n * enter the gate early and race later\" says something, and a visitor's\n * instruction names no place the owner fills (\"Fill in the form on the\n * Contact page\"); both are kept.\n */\nconst OWNER_INSTRUCTION = new RegExp(\n `${INSTRUCTION_START}(?:${OWNER_VERB}\\\\b[^.!?]{0,80}?\\\\b(?:here|later|soon|in data)\\\\b|(?:replace|swap)\\\\b[^.!?]{0,60}?\\\\byour (?:own|real)\\\\b)`,\n 'i',\n)\n\n/** What a record says of itself, which an instruction names as the thing to add: \"Add finishing position\". */\nconst RECORD_PARTS =\n '(?:position|placing|placement|result|results|score|scores|time|times|finish|date|dates|details?|info|information|name|names|story|bio|biography|description|photo|photos|picture|image|link|price|location|address|phone|email|text|copy|content|notes?|summary|title|caption|quote)'\n\n/** Words that are nothing but a bare instruction naming a part of the record: \"Add finishing position\", \"Enter result\". */\nconst BARE_INSTRUCTION = new RegExp(\n `${INSTRUCTION_START}${OWNER_VERB}\\\\s+(?:(?:the|this|that|a|an|their|its|each)\\\\s+)?(?:[\\\\p{L}'’-]+\\\\s+){0,2}${RECORD_PARTS}\\\\s*[.!]?\\\\s*$`,\n 'iu',\n)\n\n/** An aside in parentheses: \"Junior rider (add details later)\". */\nconst ASIDE = /\\s*\\(([^()]{1,160})\\)/g\n\n/** Where a sentence turns to something else: a dash, a colon, a semicolon. */\nconst TURN = /\\s*(?:[—–:;]|\\s-)\\s*/g\n\n/** A value's sentences, each with its closing mark. */\nconst sentencesOf = (value: string): string[] =>\n value\n .split(/(?<=[.!?])\\s+/)\n .map((sentence) => sentence.trim())\n .filter(Boolean)\n\n/** What a visitor's instruction points at, which an owner's never does: \"Fill in the form here\", \"Book on the Contact page\". */\nconst VISITOR_TARGET = /\\b(?:form|button|link|page|website|checkout|cart|account|app)s?\\b/i\n\n/** Whether words, read from their start, are an instruction to the owner rather than words about the record (AGL-4092). */\nexport function aiDatasetInstructionSentence(words: string): boolean {\n return (OWNER_INSTRUCTION.test(words) && !VISITOR_TARGET.test(words)) || BARE_INSTRUCTION.test(words)\n}\n\n/** One sentence without the instruction it is or ends with: \"\" for \"Add finishing position\", \"Junior rider\" for \"Junior rider — add details later\". */\nfunction sentenceWithoutInstruction(sentence: string): string {\n if (aiDatasetInstructionSentence(sentence)) return ''\n for (const turn of sentence.matchAll(TURN)) {\n const at = turn.index ?? 0\n if (at > 0 && aiDatasetInstructionSentence(sentence.slice(at + turn[0].length))) {\n const head = sentence.slice(0, at).replace(/[\\s,]+$/, '')\n return head && !/[.!?]$/.test(head) ? `${head}.` : head\n }\n }\n return sentence\n}\n\n/**\n * A value with every instruction to the owner taken out (AGL-4092): an aside,\n * a sentence, or the turn a sentence ends on; `''` where nothing else was\n * said, and the value unchanged where none was. Pure.\n */\nexport function aiDatasetValueWithoutInstructions(value: string): string {\n const unasided = value.replace(ASIDE, (aside, inner: string) => (aiDatasetInstructionSentence(inner) ? '' : aside))\n const sentences = sentencesOf(unasided)\n const kept = sentences.map(sentenceWithoutInstruction).filter(Boolean)\n return unasided === value && kept.length === sentences.length && kept.every((sentence, index) => sentence === sentences[index])\n ? value\n : kept.join(' ')\n}\n\n/** Whether a value tells the owner to do something anywhere in it (AGL-4092). */\nconst holdsInstruction = (value: string): boolean => aiDatasetValueWithoutInstructions(value) !== value\n\n/** A name that ends in a number, with any aside after it: \"Upcoming race 1 (details to be added)\". */\nconst NUMBERED_NAME = /^(.*?)\\s*(?:#|no\\.?\\s*)?\\d+\\s*(?:\\([^)]*\\))?$/i\n\n/** Words a numbered placeholder is made of besides the list's own: \"Item 1\", \"Upcoming event 2\", \"New entry 3\". */\nconst STAND_IN_WORDS = new Set(\n 'item entry record row event name product thing option sample example test placeholder untitled new upcoming past next previous future current recent old'.split(' '),\n)\n\n/** A name's words as a stem compares them: lowercase, a plural's \"-s\" dropped. */\nconst stemWords = (text: string) =>\n text\n .toLowerCase()\n .split(/[^\\p{L}\\p{N}]+/u)\n .filter(Boolean)\n .map((word) => (word.length > 3 && word.endsWith('s') && !word.endsWith('ss') ? word.slice(0, -1) : word))\n\n/**\n * The records of a designed dataset that are placeholders (AGL-4065), by\n * position, which no site shows:\n * - any value that marks a gap: \"details to be added\", \"TBD\", \"coming soon\";\n * - any sentence that tells the owner what to add instead of saying it\n * (AGL-4092): \"Add this rider's story here.\", \"Add finishing position\";\n * - a name that is a number after nothing but the list's own words and\n * stand-in words: \"Item 1\", \"Race 2\", \"Upcoming race 3\", \"New entry 4\".\n * A course's \"Level 1\" to \"Level 3\" and a series' \"Series Round 1 at\n * Lockhart\" name things, and are kept. Pure.\n */\nexport function aiDatasetPlaceholderRecords(\n dataset: { fields: ReadonlyArray<{ name: string }>; records: ReadonlyArray<ReadonlyArray<string | undefined>> },\n name = '',\n): number[] {\n const own = new Set([...stemWords(name), ...dataset.fields.flatMap((field) => stemWords(field.name))])\n return dataset.records.flatMap((values, index) => {\n if (values.some((value) => PLACEHOLDER_TEXT.test(value ?? '') || holdsInstruction(value ?? ''))) return [index]\n const match = NUMBERED_NAME.exec((values[0] ?? '').replace(/\\s+/g, ' ').trim())\n if (!match) return []\n const words = stemWords(match[1] ?? '')\n return words.every((word) => own.has(word) || STAND_IN_WORDS.has(word)) ? [index] : []\n })\n}\n\n/** An answer's fields and records as the placeholder test reads them, whatever else is wrong with it. */\nfunction answerShape(answer: Record<string, unknown> | null | undefined): {\n fields: Array<{ name: string }>\n records: string[][]\n} {\n const text = (value: unknown) => (typeof value === 'string' ? value.replace(/\\s+/g, ' ').trim() : '')\n const fields = (Array.isArray(answer?.['fields']) ? (answer?.['fields'] as unknown[]) : []).map((field) => ({\n name: text((field as Record<string, unknown> | null)?.['name']),\n }))\n const records = (Array.isArray(answer?.['records']) ? (answer?.['records'] as unknown[]) : []).map((record) => {\n const values = (record as Record<string, unknown> | null)?.['values']\n return Array.isArray(values) ? values.map(text) : []\n })\n return { fields, records }\n}\n\n/** Where a dataset is checked: its plan's fields, the words a claim may come from, and its name. */\nexport interface AiDatasetCheckContext {\n planned: readonly string[]\n merchantWords: string\n name: string\n}\n\n/**\n * A dataset's answer held to the tool's check and to naming real things\n * (AGL-4065): one with placeholder records is refused, with\n * {@link AI_DATASET_PLACEHOLDER_REASK} for the one re-ask. Pure.\n */\nexport function checkAiDatasetRecords(answer: Record<string, unknown>, context: AiDatasetCheckContext): AiGenerationCheckResult<AiDataset> {\n const checked = checkAiDataset(answer, context)\n const shape = answerShape(answer)\n const placeholders = aiDatasetPlaceholderRecords(shape, context.name)\n // A sample is of its kind, never of a make the brief does not name (AGL-4065).\n const rawRecords = Array.isArray(answer['records']) ? (answer['records'] as unknown[]) : []\n const branded = shape.records.flatMap((values, index) => {\n const sample = (rawRecords[index] as Record<string, unknown> | null)?.['sample'] === true\n const brand = sample ? values.map((value) => aiTextNamesBrand(value, context.merchantWords)).find(Boolean) : null\n return brand ? [{ index, brand }] : []\n })\n if (!placeholders.length && !branded.length) return checked\n const violations = [...checked.violations]\n const offending: Record<string, unknown> = { ...(checked.offending ?? {}) }\n if (placeholders.length) {\n violations.push({\n rule: null,\n code: AI_DATASET_PLACEHOLDER_CODE,\n message: AI_DATASET_PLACEHOLDER_REASK,\n paths: placeholders.map((index) => `records[${index}]`),\n } satisfies AiDoctrineViolation)\n for (const index of placeholders.slice(0, 3)) offending[`records[${index}]`] = shape.records[index]?.[0] ?? ''\n }\n for (const { index, brand } of branded) {\n violations.push({\n rule: null,\n code: 'sample-brand',\n message: `Sample record ${index + 1} names the make \"${brand}\", which the brief does not: a sample is of its kind, never of a brand.`,\n paths: [`records[${index}]`],\n })\n }\n return { value: null, violations, offending }\n}\n\n/**\n * A dataset answered with placeholders after its one re-ask, kept with the\n * records that name real things (AGL-4065) — however few, none included —\n * where nothing else in it breaks a rule; `null` otherwise. Pure.\n */\nexport function aiDatasetWithoutPlaceholders(answer: Record<string, unknown> | null | undefined, context: AiDatasetCheckContext): AiDataset | null {\n if (!answer) return null\n const drop = new Set(aiDatasetPlaceholderRecords(answerShape(answer), context.name))\n const records = Array.isArray(answer['records']) ? (answer['records'] as unknown[]) : []\n const kept = { ...answer, records: records.filter((_record, index) => !drop.has(index)) }\n // The fewest records a list is asked for is for one the brief fills; what is left of this one is what it gave.\n const checked = checkAiDataset(kept, { ...context, recordsMin: 1 })\n return checked.value && !checked.violations.length ? checked.value : null\n}\n\n/**\n * An answer with every instruction to the owner taken out of its records'\n * values (AGL-4092), for the salvage after its one re-ask: \"Add this rider's\n * story here.\" leaves the bio's other sentences, \"Add finishing position\"\n * leaves the result \"\", and a record with no name left is dropped. The\n * answer unchanged where no value instructed. Pure.\n */\nexport function aiDatasetWithoutInstructions<A extends Record<string, unknown> | null | undefined>(answer: A): A {\n if (!answer || !Array.isArray(answer['records'])) return answer\n let changed = false\n const records = (answer['records'] as unknown[]).flatMap((record) => {\n const values = (record as Record<string, unknown> | null)?.['values']\n if (!Array.isArray(values)) return [record]\n const next = values.map((value: unknown) => (typeof value === 'string' ? aiDatasetValueWithoutInstructions(value) : value))\n if (next.every((value, index) => value === values[index])) return [record]\n changed = true\n return typeof next[0] === 'string' && next[0].trim() ? [{ ...(record as Record<string, unknown>), values: next }] : []\n })\n return changed ? ({ ...answer, records } as A) : answer\n}\n\n/** A designed dataset with every instruction to the owner taken out (AGL-4092), as {@link aiDatasetWithoutInstructions} does an answer. */\nfunction designedWithoutInstructions(dataset: AiDataset): AiDataset {\n const records = dataset.records.map((values) => values.map((value) => aiDatasetValueWithoutInstructions(value ?? '')))\n if (records.every((values, index) => values.every((value, column) => value === (dataset.records[index]?.[column] ?? '')))) return dataset\n const keep = records.map((values) => Boolean(values[0]?.trim()))\n const { samples: all, ...rest } = dataset\n const samples = all?.filter((_sample, index) => keep[index])\n return { ...rest, records: records.filter((_values, index) => keep[index]), ...(samples?.some(Boolean) ? { samples } : {}) }\n}\n\n/**\n * A designed dataset with any instruction to the owner (AGL-4092) and any\n * placeholder record taken out (AGL-4065), and where the list starts with no\n * samples (`AI_DATASET_LIST_WITHOUT_ENTRIES` is `empty`), every sample too.\n * Pure.\n */\nexport function aiDatasetRealRecords(designed: AiDataset, name: string): AiDataset {\n const dataset = designedWithoutInstructions(designed)\n const drop = new Set(aiDatasetPlaceholderRecords(dataset, name))\n if (AI_DATASET_LIST_WITHOUT_ENTRIES === 'empty') dataset.samples?.forEach((sample, index) => sample && drop.add(index))\n if (!drop.size) return dataset\n const keep = (_entry: unknown, index: number) => !drop.has(index)\n const { samples: all, ...rest } = dataset\n const samples = all?.filter(keep)\n return { ...rest, records: dataset.records.filter(keep), ...(samples?.some(Boolean) ? { samples } : {}) }\n}\n\n// ── A sample row's date (AGL-4065) ──────────────────────────────────────\n\n/** A field whose name says it holds when a record happens. */\nconst DATE_FIELD = /\\b(?:date|dates|day|when)\\b/i\n\n/** A field a record has only once it has happened: a race's result, a match's score. */\nconst OUTCOME_FIELD = /\\b(?:results?|finish(?:ed)?|placings?|placed|winners?|scores?|outcome)\\b/i\n\n/** A value or a section's name that says its records are behind us. */\nconst PAST_WORDS = /\\b(?:past|previous|completed|finished|results?|earlier|archived?|last season)\\b/i\n\n/** The days a sample's date steps by, and how far from the job's day the first falls: a season of weekends. */\nexport const AI_DATASET_SAMPLE_DATES = { stepDays: 21, leadDays: 14 } as const\n\nconst DAY_MS = 86_400_000\n\n/** The Saturday on or after (`ahead`) or on or before a day, at noon UTC. */\nfunction saturday(from: Date, ahead: boolean): Date {\n const day = new Date(Date.UTC(from.getUTCFullYear(), from.getUTCMonth(), from.getUTCDate(), 12))\n const shift = ahead ? (6 - day.getUTCDay() + 7) % 7 : -((day.getUTCDay() + 1) % 7)\n return new Date(day.getTime() + shift * DAY_MS)\n}\n\n/**\n * A dated list's SAMPLE records given real dates (AGL-4065): a sample the\n * brief gave no date for falls on a weekend of a season from the job's day —\n * the first Saturday at least two weeks out, then every three weeks — and a\n * sample that is behind us (its split value or the list says past: \"Past\n * results\") on the Saturdays as far back, the latest first, with no result\n * left on one still to come. Written as a visitor reads a date, in the\n * site's language. A record from the brief keeps its own date. Pure.\n */\nexport function aiDatasetSampleDates(\n dataset: AiDataset,\n options: { now: Date; language?: string | null; split?: AiDatasetSplit | null; pastOnly?: boolean },\n): AiDataset {\n const samples = dataset.samples ?? []\n const dates = dataset.fields.flatMap((field, column) => (column > 0 && field.type === 'text' && DATE_FIELD.test(field.name) ? [column] : []))\n if (!samples.some(Boolean) || !dates.length) return dataset\n const splitColumn = options.split\n ? dataset.fields.findIndex((field) => field.name.trim().toLowerCase() === (options.split as AiDatasetSplit).field.trim().toLowerCase())\n : -1\n const outcomes = dataset.fields.flatMap((field, column) => (column > 0 && OUTCOME_FIELD.test(field.name) ? [column] : []))\n let format: Intl.DateTimeFormat\n try {\n format = new Intl.DateTimeFormat(options.language || 'en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC' })\n } catch {\n format = new Intl.DateTimeFormat('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric', timeZone: 'UTC' })\n }\n const { stepDays, leadDays } = AI_DATASET_SAMPLE_DATES\n const firstAhead = saturday(new Date(options.now.getTime() + leadDays * DAY_MS), true)\n const firstBehind = saturday(new Date(options.now.getTime() - leadDays * DAY_MS), false)\n let ahead = 0\n let behind = 0\n return {\n ...dataset,\n records: dataset.records.map((values, index) => {\n if (!samples[index]) return [...values]\n const past = options.pastOnly || (splitColumn > 0 && PAST_WORDS.test(values[splitColumn] ?? ''))\n const at = past\n ? new Date(firstBehind.getTime() - behind++ * stepDays * DAY_MS)\n : new Date(firstAhead.getTime() + ahead++ * stepDays * DAY_MS)\n const said = format.format(at)\n // A race still to come has no result, and one behind us never an instruction for one (AGL-4092).\n return values.map((value, column) =>\n dates.includes(column) ? said : outcomes.includes(column) && (!past || holdsInstruction(value ?? '')) ? '' : value,\n )\n }),\n }\n}\n\n/** Whether every section that lists a dataset says its records are behind us (\"Past results\", \"Race results\"). */\nexport function aiDatasetListedAsPast(sections: readonly string[]): boolean {\n return sections.length > 0 && sections.every((section) => PAST_WORDS.test(section))\n}\n\nexport interface GenerateAiDatasetInput extends AiDatasetPromptInput {\n /** What a claim or a price may be quoted from: the brief and the site's answers. */\n merchantWords: string\n settings?: AiPluginSettings\n model?: string\n maxTokens?: number\n signal?: AbortSignal\n provider?: AiProvider\n}\n\n/** Design one dataset. It writes nothing to the site. */\nexport function generateAiDataset(input: GenerateAiDatasetInput): Promise<AiValidatedGeneration<AiDataset>> {\n const row = AI_ROUTING_TABLE[AI_DATASET_STEP]\n return runValidatedGeneration(AI_DATASET_KIND, {\n step: AI_DATASET_STEP,\n ...(input.settings ? { settings: input.settings } : {}),\n ...(input.model ? { model: input.model } : {}),\n instructions: AI_DATASET_INSTRUCTIONS,\n messages: [{ role: 'user', content: aiDatasetPrompt(input) }],\n tool: AI_DATASET_TOOL,\n maxTokens: input.maxTokens ?? AI_DATASET_MAX_TOKENS,\n ...(row.thinking ? { thinking: row.thinking } : {}),\n ...(row.effort ? { effort: row.effort } : {}),\n ...(input.signal ? { signal: input.signal } : {}),\n ...(input.provider ? { provider: input.provider } : {}),\n check: (answer) => checkAiDatasetRecords(answer, { planned: input.fields, merchantWords: input.merchantWords, name: input.name }),\n }) as Promise<AiValidatedGeneration<AiDataset>>\n}\n\n/**\n * A designed dataset as the data plugin's `dataset` writer takes it: values by\n * field name. Its words' \"a\" and \"an\" agree with what follows them in English\n * (`aiLayoutArticles`, AGL-3988), as a page's do: the beta.270 Sugar Hollow\n * packages said \"A easygoing session\" in their records and on every page\n * that listed them.\n */\nexport function aiDatasetDraftContent(\n name: string,\n dataset: AiDataset,\n options: { recordPages: boolean; language?: string | null },\n): Record<string, unknown> {\n const said = (value: string) => aiLayoutArticles(value, options.language)\n const records = dataset.records.map((values) => {\n const record: Record<string, unknown> = {}\n dataset.fields.forEach((field, column) => {\n const value = (values[column] ?? '').trim()\n if (!value) return\n if (field.type === 'list') {\n record[field.name] = value.split(AI_DATASET_LIST_SEPARATOR.trim()).map((part) => said(part.trim())).filter(Boolean)\n } else if (field.type === 'number' || field.type === 'integer') {\n record[field.name] = value.replace(/,/g, '')\n } else if (field.type === 'text') {\n record[field.name] = said(value)\n } else {\n record[field.name] = value\n }\n })\n return record\n })\n return {\n name,\n fields: dataset.fields.map((field) => ({ name: field.name, type: field.type })),\n records,\n // Which records are samples for the owner to replace (AGL-4065), which Data marks.\n ...(dataset.samples?.some(Boolean) ? { samples: dataset.records.map((_values, index) => dataset.samples?.[index] === true) } : {}),\n // A record page shows each record at an address made from its name.\n ...(options.recordPages && dataset.fields[0] ? { pageAddressFrom: dataset.fields[0].name } : {}),\n }\n}\n"],"names":["AI_ROUTING_TABLE","AI_DATASET_LIMITS","AI_DATASET_LIST_SEPARATOR","AI_DATASET_TOOL","AI_DATASET_TOOL_NAME","checkAiDataset","runValidatedGeneration","aiLayoutArticles","aiTextNamesBrand","AI_DATASET_STEP","AI_DATASET_MAX_TOKENS","Math","min","maxTokens","AI_DATASET_KIND","AI_DATASET_LIST_WITHOUT_ENTRIES","WITHOUT_ENTRIES_RULE","AI_DATASET_INSTRUCTIONS","text","recordsMin","recordsMax","valueMax","map","rule","join","cacheBreakpoint","listed","names","last","quoted","name","length","slice","aiDatasetSplitLine","split","sections","field","values","aiDatasetPrompt","input","brief","trim","why","fields","shownIn","recordPages","AI_DATASET_PLACEHOLDER_REASK","AI_DATASET_PLACEHOLDER_CODE","PLACEHOLDER_TEXT","OWNER_VERB","INSTRUCTION_START","OWNER_INSTRUCTION","RegExp","RECORD_PARTS","BARE_INSTRUCTION","ASIDE","TURN","sentencesOf","value","sentence","filter","Boolean","VISITOR_TARGET","aiDatasetInstructionSentence","words","test","sentenceWithoutInstruction","turn","matchAll","at","index","head","replace","aiDatasetValueWithoutInstructions","unasided","aside","inner","sentences","kept","every","holdsInstruction","NUMBERED_NAME","STAND_IN_WORDS","Set","stemWords","toLowerCase","word","endsWith","aiDatasetPlaceholderRecords","dataset","own","flatMap","records","match","some","exec","has","answerShape","answer","Array","isArray","record","checkAiDatasetRecords","context","checked","shape","placeholders","rawRecords","branded","sample","brand","merchantWords","find","violations","offending","push","code","message","paths","aiDatasetWithoutPlaceholders","drop","_record","aiDatasetWithoutInstructions","changed","next","designedWithoutInstructions","column","keep","samples","all","rest","_sample","_values","aiDatasetRealRecords","designed","forEach","add","size","_entry","DATE_FIELD","OUTCOME_FIELD","PAST_WORDS","AI_DATASET_SAMPLE_DATES","stepDays","leadDays","DAY_MS","saturday","from","ahead","day","Date","UTC","getUTCFullYear","getUTCMonth","getUTCDate","shift","getUTCDay","getTime","aiDatasetSampleDates","options","dates","type","splitColumn","findIndex","outcomes","format","Intl","DateTimeFormat","language","weekday","month","year","timeZone","firstAhead","now","firstBehind","behind","past","pastOnly","said","includes","aiDatasetListedAsPast","section","generateAiDataset","row","step","settings","model","instructions","messages","role","content","tool","thinking","effort","signal","provider","check","planned","aiDatasetDraftContent","part","pageAddressFrom"],"mappings":"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"}
|
|
@@ -62,6 +62,7 @@ export interface AiReplayRequest {
|
|
|
62
62
|
maxTokens: number;
|
|
63
63
|
thinking?: AiProviderRequest['thinking'];
|
|
64
64
|
effort?: AiProviderRequest['effort'];
|
|
65
|
+
toolChoice?: AiProviderRequest['toolChoice'];
|
|
65
66
|
}
|
|
66
67
|
/** The request's replay key: a SHA-256 of everything that decides the answer. */
|
|
67
68
|
export declare function aiDevReplayKey(providerId: string, stream: boolean, request: AiReplayRequest): string;
|