@aglyn/plugins-ai 1.0.0-beta.272 → 1.0.0-beta.274

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.
Files changed (52) hide show
  1. package/package.json +17 -17
  2. package/src/lib/components/ai-site-start-card.component.js +237 -234
  3. package/src/lib/components/ai-site-start-card.component.js.map +1 -1
  4. package/src/lib/jobs/ai-empty-site-sweep.d.ts +12 -1
  5. package/src/lib/jobs/ai-empty-site-sweep.js +15 -3
  6. package/src/lib/jobs/ai-empty-site-sweep.js.map +1 -1
  7. package/src/lib/jobs/ai-job-page-step.js +21 -2
  8. package/src/lib/jobs/ai-job-page-step.js.map +1 -1
  9. package/src/lib/jobs/ai-job-seo-step.js +8 -4
  10. package/src/lib/jobs/ai-job-seo-step.js.map +1 -1
  11. package/src/lib/jobs/ai-job-site-datasets.d.ts +39 -1
  12. package/src/lib/jobs/ai-job-site-datasets.js +111 -15
  13. package/src/lib/jobs/ai-job-site-datasets.js.map +1 -1
  14. package/src/lib/jobs/ai-job-site-step.d.ts +12 -3
  15. package/src/lib/jobs/ai-job-site-step.js +85 -11
  16. package/src/lib/jobs/ai-job-site-step.js.map +1 -1
  17. package/src/lib/jobs/ai-job-text-step.d.ts +8 -0
  18. package/src/lib/jobs/ai-job-text-step.js.map +1 -1
  19. package/src/lib/jobs/ai-jobs.d.ts +3 -0
  20. package/src/lib/jobs/ai-jobs.js +4 -0
  21. package/src/lib/jobs/ai-jobs.js.map +1 -1
  22. package/src/lib/jobs/ai-layout-owner-photos.js +4 -4
  23. package/src/lib/jobs/ai-layout-owner-photos.js.map +1 -1
  24. package/src/lib/jobs/ai-layout-stock-photos.d.ts +35 -0
  25. package/src/lib/jobs/ai-layout-stock-photos.js +114 -43
  26. package/src/lib/jobs/ai-layout-stock-photos.js.map +1 -1
  27. package/src/lib/jobs/ai-site-late-photos.d.ts +143 -0
  28. package/src/lib/jobs/ai-site-late-photos.js +486 -0
  29. package/src/lib/jobs/ai-site-late-photos.js.map +1 -0
  30. package/src/lib/jobs/ai-site-publish.d.ts +18 -0
  31. package/src/lib/jobs/ai-site-publish.js +123 -5
  32. package/src/lib/jobs/ai-site-publish.js.map +1 -1
  33. package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.d.ts +83 -0
  34. package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js +1553 -0
  35. package/src/lib/jobs/fixtures/ai-site-late-photos-laurel-fork.js.map +1 -0
  36. package/src/lib/layout-language/ai-layout-pictures.d.ts +50 -13
  37. package/src/lib/layout-language/ai-layout-pictures.js +81 -19
  38. package/src/lib/layout-language/ai-layout-pictures.js.map +1 -1
  39. package/src/lib/model/ai-jobs.types.d.ts +10 -0
  40. package/src/lib/model/ai-jobs.types.js.map +1 -1
  41. package/src/lib/model/ai-site-photos.d.ts +119 -0
  42. package/src/lib/model/ai-site-photos.js +128 -0
  43. package/src/lib/model/ai-site-photos.js.map +1 -0
  44. package/src/lib/runtime/ai-doctrine-validators.d.ts +3 -1
  45. package/src/lib/runtime/ai-doctrine-validators.js +3 -1
  46. package/src/lib/runtime/ai-doctrine-validators.js.map +1 -1
  47. package/src/lib/runtime/seo-fields.d.ts +29 -4
  48. package/src/lib/runtime/seo-fields.js +58 -11
  49. package/src/lib/runtime/seo-fields.js.map +1 -1
  50. package/src/lib/tools/ai-seo-tool.d.ts +25 -1
  51. package/src/lib/tools/ai-seo-tool.js +61 -0
  52. package/src/lib/tools/ai-seo-tool.js.map +1 -1
@@ -14,7 +14,8 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
14
14
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
15
  * See the License for the specific language governing permissions and
16
16
  * limitations under the License.
17
- */ import { checkDatasetQuota } from "@aglyn/aglyn/app-utils/plan-entitlements";
17
+ */ import { mediaRefFromCdnPath } from "@aglyn/aglyn/app-utils/media-ref";
18
+ import { checkDatasetQuota } from "@aglyn/aglyn/app-utils/plan-entitlements";
18
19
  import { pluginResourceDraftWriter } from "@aglyn/aglyn/plugin-manager/plugin-resource-drafts";
19
20
  import { pluginRecordIndex } from "@aglyn/aglyn/plugin-manager/plugin-record-index";
20
21
  import { AI_BUILD_PLAN_LIMITS, AI_PLAN_NEW_REF_PREFIX, isAiPlanNewRef } from "../model/ai-build-plan.js";
@@ -26,6 +27,7 @@ import { AI_DATASET_MAX_TOKENS, AI_DATASET_STEP, aiDatasetDraftContent, generate
26
27
  import { aiLayoutListingId, aiLayoutRecordBaseOf, aiLayoutRecordsPlacements } from "../layout-language/ai-layout-listings.js";
27
28
  import { aiLayoutNeutralStarterRun, aiLayoutSeedNumber, aiLayoutStarterPhotos } from "../layout-language/ai-layout-pictures.js";
28
29
  import { aiSiteWords } from "../model/ai-site-job.js";
30
+ import { aiSiteIsStarterSrc } from "../model/ai-site-photos.js";
29
31
  import { aiOriginJobId } from "./ai-job-draft-ids.js";
30
32
  import { aiSiteProductPhotoSrc } from "./ai-job-site-content.js";
31
33
  import { aiLayoutStockPhotoSource, aiStockJobPhotosOf, aiStockPhotoWordsOf, aiStockRecordScene, aiStockStem } from "./ai-layout-stock-photos.js";
@@ -267,6 +269,9 @@ const limitReview = (message)=>({
267
269
  });
268
270
  const seed = `${job.$id}:records`;
269
271
  let found = [];
272
+ /** The picks the image hosts refused, by record (AGL-3988). */ const report = {
273
+ refused: new Map()
274
+ };
270
275
  /** Whether the site has a stock library: its records then never take a starter that shows another business (AGL-3988). */ let library = false;
271
276
  try {
272
277
  var _input_stockPhotos;
@@ -288,7 +293,7 @@ const limitReview = (message)=>({
288
293
  budgetMs: AI_SITE_DATASET_PHOTOS_BUDGET_MS
289
294
  });
290
295
  library = Boolean(source);
291
- if (source) found = await source(slots);
296
+ if (source) found = await source(slots, report);
292
297
  } catch (error) {
293
298
  console.warn('ai site dataset: the stock photos failed; the job’s own photos or starters fill them', {
294
299
  error: String(error)
@@ -328,11 +333,16 @@ const limitReview = (message)=>({
328
333
  height: photo.height
329
334
  };
330
335
  }
331
- return {
336
+ const refused = report.refused.get(slots[index]);
337
+ return _extends({
332
338
  src: aiSiteProductPhotoSrc((_starters_index = starters[index]) == null ? void 0 : _starters_index.src, hostId),
333
339
  placed: null,
334
340
  alt: ((_starters_index1 = starters[index]) == null ? void 0 : (_starters_index_alt = _starters_index1.alt) == null ? void 0 : _starters_index_alt.trim()) || null
335
- };
341
+ }, refused ? {
342
+ refused
343
+ } : {}, library ? {} : {
344
+ noLibrary: true
345
+ });
336
346
  });
337
347
  }
338
348
  /**
@@ -351,16 +361,33 @@ const limitReview = (message)=>({
351
361
  * photo the list shows. Never the photo of the record beside it while
352
362
  * another fits, the least shown first, the seed picking among equals. A
353
363
  * record no photo fits keeps none, and takes a starter. Pure.
354
- */ export function aiSiteDatasetRecordsFromOwn(records, photos, seed) {
355
- const own = photos.flatMap((photo, index)=>(photo == null ? void 0 : photo.src) ? [
364
+ *
365
+ * `besideLast` is the site start's late photo pass (AGL-3988): there the
366
+ * other choice is a starter photo of another business, so a record every
367
+ * photo sits beside takes the photo beside it rather than none.
368
+ */ export function aiSiteDatasetRecordsFromOwn(records, photos, seed, /**
369
+ * The photos the job copied for its pages (AGL-3988), which the site
370
+ * start's late photo pass offers a list whose own photos were all refused:
371
+ * filed under no category, so each is taken by the people it shows.
372
+ */ others = [], options = {}) {
373
+ const listed = photos.flatMap((photo, index)=>(photo == null ? void 0 : photo.src) ? [
356
374
  {
357
375
  photo,
358
376
  index
359
377
  }
360
378
  ] : []);
379
+ const shownHere = new Set(listed.map((entry)=>entry.photo.src));
380
+ const own = [
381
+ ...listed,
382
+ ...others.filter((photo, at, all)=>photo.src && !shownHere.has(photo.src) && all.findIndex((one)=>one.src === photo.src) === at).map((photo, at)=>({
383
+ photo,
384
+ index: records.length + at
385
+ }))
386
+ ];
361
387
  if (!own.length) return [
362
388
  ...photos
363
389
  ];
390
+ /** Every place a photo is ranked from: the list's records, then the job's other photos. */ const span = records.length + own.length - listed.length;
364
391
  const category = (index)=>{
365
392
  var _ref;
366
393
  var _records_index_category, _records_index;
@@ -394,11 +421,12 @@ const limitReview = (message)=>({
394
421
  subject.filter(apart),
395
422
  same,
396
423
  subject,
397
- own.filter(apart)
424
+ own.filter(apart),
425
+ options.besideLast ? own : []
398
426
  ].find((list)=>list.length);
399
427
  if (!pool) continue;
400
428
  const uses = (src)=>filled.filter((photo)=>(photo == null ? void 0 : photo.src) === src).length;
401
- const rank = (at)=>((at - turn) % records.length + records.length) % records.length;
429
+ const rank = (at)=>((at - turn) % span + span) % span;
402
430
  const [pick] = [
403
431
  ...pool
404
432
  ].sort((a, b)=>uses(a.photo.src) - uses(b.photo.src) || rank(a.index) - rank(b.index));
@@ -562,7 +590,7 @@ function proposalOf(output) {
562
590
  const writerFor = (_deps_writerFor = deps.writerFor) != null ? _deps_writerFor : pluginResourceDraftWriter;
563
591
  const photosFor = (_deps_photos = deps.photos) != null ? _deps_photos : aiSiteDatasetRecordPhotos;
564
592
  return async (context)=>{
565
- var _ref, _context_org, _facts_fields, _facts_addressField, _facts_fields1, _facts_fields2, _facts_records;
593
+ var _ref, _context_org, _facts_fields, _facts_addressField, _facts_fields1, _facts_fields2, _facts_imageField, _facts_fields3, _ref1, _facts_records;
566
594
  var _job_plan_create, _job_plan, _job_plan1;
567
595
  const { job, firestore, signal } = context;
568
596
  const model = (_ref = context.modelFor == null ? void 0 : context.modelFor.call(context, AI_DATASET_STEP)) != null ? _ref : aiModelForStep(AI_DATASET_STEP);
@@ -596,13 +624,13 @@ function proposalOf(output) {
596
624
  id: job.$id
597
625
  });
598
626
  if (written) {
599
- var _facts_fields3, _facts_addressField1, _facts_imageField;
627
+ var _facts_fields4, _facts_addressField1, _facts_imageField1;
600
628
  const facts = written.facts;
601
629
  const again = output(written.id, written.name || creation.name, {
602
- fields: (_facts_fields3 = facts.fields) != null ? _facts_fields3 : [],
630
+ fields: (_facts_fields4 = facts.fields) != null ? _facts_fields4 : [],
603
631
  recordNames: [],
604
632
  addressField: (_facts_addressField1 = facts.addressField) != null ? _facts_addressField1 : null,
605
- imageField: (_facts_imageField = facts.imageField) != null ? _facts_imageField : null
633
+ imageField: (_facts_imageField1 = facts.imageField) != null ? _facts_imageField1 : null
606
634
  }, 0);
607
635
  return aiUnspentOutcome(model, {
608
636
  outputs: [
@@ -615,7 +643,7 @@ function proposalOf(output) {
615
643
  // A form's dataset (AGL-3616): its records are the submissions, so it is
616
644
  // designed from the plan with no model, and starts empty.
617
645
  if (input.forForm) {
618
- var _context_org1, _facts_fields4;
646
+ var _context_org1, _facts_fields5;
619
647
  const content = aiSiteFormDatasetContent(creation.name, creation.fields, input.forForm);
620
648
  const checked = keeper.writer.check(content, {
621
649
  hostId: job.hostId
@@ -652,7 +680,7 @@ function proposalOf(output) {
652
680
  return aiUnspentOutcome(model, {
653
681
  outputs: [
654
682
  _extends({}, output(result.id, result.name, {
655
- fields: (_facts_fields4 = facts.fields) != null ? _facts_fields4 : [],
683
+ fields: (_facts_fields5 = facts.fields) != null ? _facts_fields5 : [],
656
684
  recordNames: [],
657
685
  addressField: null
658
686
  }, 0), {
@@ -776,13 +804,81 @@ function proposalOf(output) {
776
804
  } : {}, {
777
805
  sparseFields: aiDatasetSparseFields(generation.value, (_facts_fields1 = facts.fields) != null ? _facts_fields1 : [])
778
806
  }, aiSiteDatasetGroupsOf(category, (_facts_fields2 = facts.fields) != null ? _facts_fields2 : [], generation.value));
807
+ // Its records left on a starter, which the site start's late photo pass fills (AGL-3988).
808
+ const starters = aiSiteDatasetStarterList({
809
+ dataset: result.id,
810
+ hostId: job.hostId,
811
+ imageField: (_facts_imageField = facts.imageField) != null ? _facts_imageField : null,
812
+ fields: (_facts_fields3 = facts.fields) != null ? _facts_fields3 : [],
813
+ people: input.pictures === 'people',
814
+ names: generation.value.records.map((values)=>{
815
+ var _values_;
816
+ return (_values_ = values[0]) != null ? _values_ : '';
817
+ }),
818
+ categories: (_ref1 = category == null ? void 0 : category.values) != null ? _ref1 : [],
819
+ photos
820
+ });
779
821
  return _extends({}, spent, {
780
822
  outputs: [
781
823
  output(result.id, result.name, proposal, (_facts_records = facts.records) != null ? _facts_records : generation.value.records.length)
782
824
  ]
783
- });
825
+ }, starters ? {
826
+ sitePhotos: {
827
+ pages: [],
828
+ lists: [
829
+ starters
830
+ ]
831
+ }
832
+ } : {});
784
833
  };
785
834
  }
835
+ /**
836
+ * A pictured list's records as the late photo pass reads them (AGL-3988),
837
+ * where any of them holds a starter on a site with a stock library: each
838
+ * record's name, category and photo
839
+ * value in order, the library photo it shows as a media reference with its
840
+ * size and words, and the picks the image hosts refused it. `null` where
841
+ * every record shows a library photo, or the list keeps no photo field.
842
+ */ export function aiSiteDatasetStarterList(input) {
843
+ var _input_fields_find;
844
+ // A site with no stock library keeps its starters, as it always has.
845
+ if (!input.imageField || !input.photos.some((photo)=>aiSiteIsStarterSrc(photo.src) && !photo.noLibrary)) return null;
846
+ const altField = (_input_fields_find = input.fields.find((field)=>field.name.trim().toLowerCase() === AI_SITE_DATASET_IMAGE_ALT_FIELD.toLowerCase())) == null ? void 0 : _input_fields_find.id;
847
+ return _extends({
848
+ dataset: input.dataset,
849
+ imageField: input.imageField
850
+ }, altField ? {
851
+ altField
852
+ } : {}, input.people ? {
853
+ people: true
854
+ } : {}, {
855
+ records: input.names.map((name, index)=>{
856
+ var _ref;
857
+ var _input_categories_index;
858
+ const photo = input.photos[index];
859
+ const value = (_ref = photo == null ? void 0 : photo.src) != null ? _ref : '';
860
+ const src = aiSiteIsStarterSrc(value) ? null : (photo == null ? void 0 : photo.placed) || mediaRefFromCdnPath(value) || null;
861
+ const category = (_input_categories_index = input.categories[index]) == null ? void 0 : _input_categories_index.trim();
862
+ return _extends({
863
+ name
864
+ }, category ? {
865
+ category
866
+ } : {}, {
867
+ value
868
+ }, src && (photo == null ? void 0 : photo.width) && photo.height ? {
869
+ photo: _extends({
870
+ src,
871
+ width: photo.width,
872
+ height: photo.height
873
+ }, photo.alt ? {
874
+ alt: photo.alt
875
+ } : {})
876
+ } : {}, (photo == null ? void 0 : photo.refused) ? {
877
+ refused: photo.refused
878
+ } : {});
879
+ })
880
+ });
881
+ }
786
882
  export const runAiSiteDatasetUnit = createAiSiteDatasetRunner();
787
883
  /**
788
884
  * Whether a dataset may be created for this member on this site, asked before