@anthusai/papyrus 5.43.0-next.46 → 5.43.0-next.47
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/amplify/data/resource.js +11 -0
- package/amplify/data/resource.ts +11 -0
- package/amplify/data/schema.js +11 -0
- package/amplify/data/schema.ts +11 -0
- package/amplify/functions/category-action/handler.ts +19 -0
- package/lib/reference-feed-keys.ts +69 -0
- package/package.json +2 -1
- package/routes.manifest.json +1 -1
package/amplify/data/resource.js
CHANGED
|
@@ -1571,6 +1571,15 @@ const schema = a.schema({
|
|
|
1571
1571
|
curationStatusReason: a.string(),
|
|
1572
1572
|
newsroomFeedKey: a.string(),
|
|
1573
1573
|
reviewedFeedKey: a.string(),
|
|
1574
|
+
// The References page reads the feed through two indexes (docs/reference-feed-indexes.md). `feedStateKey` is
|
|
1575
|
+
// "<corpusId>#unrated" or "<corpusId>#rated" for a current, not archived Reference (null otherwise: it is in no feed).
|
|
1576
|
+
// `predictionSortKey` orders Best bets, `feedArrivalAt` orders Newest. The prediction is the Cyclotron's ORIGINAL
|
|
1577
|
+
// P(accept), written once, and never rewritten by a vote.
|
|
1578
|
+
feedStateKey: a.string(),
|
|
1579
|
+
feedArrivalAt: a.datetime(),
|
|
1580
|
+
predictedAcceptProbability: a.float(),
|
|
1581
|
+
predictionDecidedAt: a.datetime(),
|
|
1582
|
+
predictionSortKey: a.string(),
|
|
1574
1583
|
updatedAt: a.datetime(),
|
|
1575
1584
|
})
|
|
1576
1585
|
.secondaryIndexes((index) => [
|
|
@@ -1583,6 +1592,8 @@ const schema = a.schema({
|
|
|
1583
1592
|
index("curationStatus").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusAndUpdatedAt"),
|
|
1584
1593
|
index("curationStatusKey").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusKeyAndUpdatedAt"),
|
|
1585
1594
|
index("reviewedFeedKey").sortKeys(["updatedAt"]).queryField("listReferencesByReviewedFeedAndUpdatedAt"),
|
|
1595
|
+
index("feedStateKey").sortKeys(["predictionSortKey"]).queryField("listReferencesByFeedStateAndPrediction"),
|
|
1596
|
+
index("feedStateKey").sortKeys(["feedArrivalAt"]).queryField("listReferencesByFeedStateAndArrival"),
|
|
1586
1597
|
])
|
|
1587
1598
|
.authorization((allow) => [
|
|
1588
1599
|
allow.groups(categoryWriteGroups).to(["read"]),
|
package/amplify/data/resource.ts
CHANGED
|
@@ -1684,6 +1684,15 @@ const schema = a.schema({
|
|
|
1684
1684
|
curationStatusReason: a.string(),
|
|
1685
1685
|
newsroomFeedKey: a.string(),
|
|
1686
1686
|
reviewedFeedKey: a.string(),
|
|
1687
|
+
// The References page reads the feed through two indexes (docs/reference-feed-indexes.md). `feedStateKey` is
|
|
1688
|
+
// "<corpusId>#unrated" or "<corpusId>#rated" for a current, not archived Reference (null otherwise: it is in no feed).
|
|
1689
|
+
// `predictionSortKey` orders Best bets, `feedArrivalAt` orders Newest. The prediction is the Cyclotron's ORIGINAL
|
|
1690
|
+
// P(accept), written once, and never rewritten by a vote.
|
|
1691
|
+
feedStateKey: a.string(),
|
|
1692
|
+
feedArrivalAt: a.datetime(),
|
|
1693
|
+
predictedAcceptProbability: a.float(),
|
|
1694
|
+
predictionDecidedAt: a.datetime(),
|
|
1695
|
+
predictionSortKey: a.string(),
|
|
1687
1696
|
updatedAt: a.datetime(),
|
|
1688
1697
|
})
|
|
1689
1698
|
.secondaryIndexes((index) => [
|
|
@@ -1696,6 +1705,8 @@ const schema = a.schema({
|
|
|
1696
1705
|
index("curationStatus").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusAndUpdatedAt"),
|
|
1697
1706
|
index("curationStatusKey").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusKeyAndUpdatedAt"),
|
|
1698
1707
|
index("reviewedFeedKey").sortKeys(["updatedAt"]).queryField("listReferencesByReviewedFeedAndUpdatedAt"),
|
|
1708
|
+
index("feedStateKey").sortKeys(["predictionSortKey"]).queryField("listReferencesByFeedStateAndPrediction"),
|
|
1709
|
+
index("feedStateKey").sortKeys(["feedArrivalAt"]).queryField("listReferencesByFeedStateAndArrival"),
|
|
1699
1710
|
])
|
|
1700
1711
|
.authorization((allow) => [
|
|
1701
1712
|
allow.groups(categoryWriteGroups).to(["read"]),
|
package/amplify/data/schema.js
CHANGED
|
@@ -1477,6 +1477,15 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
1477
1477
|
curationStatusReason: a.string(),
|
|
1478
1478
|
newsroomFeedKey: a.string(),
|
|
1479
1479
|
reviewedFeedKey: a.string(),
|
|
1480
|
+
// The References page reads the feed through two indexes (docs/reference-feed-indexes.md). `feedStateKey` is
|
|
1481
|
+
// "<corpusId>#unrated" or "<corpusId>#rated" for a current, not archived Reference (null otherwise: it is in no feed).
|
|
1482
|
+
// `predictionSortKey` orders Best bets, `feedArrivalAt` orders Newest. The prediction is the Cyclotron's ORIGINAL
|
|
1483
|
+
// P(accept), written once, and never rewritten by a vote.
|
|
1484
|
+
feedStateKey: a.string(),
|
|
1485
|
+
feedArrivalAt: a.datetime(),
|
|
1486
|
+
predictedAcceptProbability: a.float(),
|
|
1487
|
+
predictionDecidedAt: a.datetime(),
|
|
1488
|
+
predictionSortKey: a.string(),
|
|
1480
1489
|
updatedAt: a.datetime(),
|
|
1481
1490
|
})
|
|
1482
1491
|
.secondaryIndexes((index) => [
|
|
@@ -1489,6 +1498,8 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
1489
1498
|
index("curationStatus").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusAndUpdatedAt"),
|
|
1490
1499
|
index("curationStatusKey").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusKeyAndUpdatedAt"),
|
|
1491
1500
|
index("reviewedFeedKey").sortKeys(["updatedAt"]).queryField("listReferencesByReviewedFeedAndUpdatedAt"),
|
|
1501
|
+
index("feedStateKey").sortKeys(["predictionSortKey"]).queryField("listReferencesByFeedStateAndPrediction"),
|
|
1502
|
+
index("feedStateKey").sortKeys(["feedArrivalAt"]).queryField("listReferencesByFeedStateAndArrival"),
|
|
1492
1503
|
])
|
|
1493
1504
|
.authorization((allow) => [
|
|
1494
1505
|
allow.groups(categoryWriteGroups).to(["read"]),
|
package/amplify/data/schema.ts
CHANGED
|
@@ -1588,6 +1588,15 @@ const definitions = {
|
|
|
1588
1588
|
curationStatusReason: a.string(),
|
|
1589
1589
|
newsroomFeedKey: a.string(),
|
|
1590
1590
|
reviewedFeedKey: a.string(),
|
|
1591
|
+
// The References page reads the feed through two indexes (docs/reference-feed-indexes.md). `feedStateKey` is
|
|
1592
|
+
// "<corpusId>#unrated" or "<corpusId>#rated" for a current, not archived Reference (null otherwise: it is in no feed).
|
|
1593
|
+
// `predictionSortKey` orders Best bets, `feedArrivalAt` orders Newest. The prediction is the Cyclotron's ORIGINAL
|
|
1594
|
+
// P(accept), written once, and never rewritten by a vote.
|
|
1595
|
+
feedStateKey: a.string(),
|
|
1596
|
+
feedArrivalAt: a.datetime(),
|
|
1597
|
+
predictedAcceptProbability: a.float(),
|
|
1598
|
+
predictionDecidedAt: a.datetime(),
|
|
1599
|
+
predictionSortKey: a.string(),
|
|
1591
1600
|
updatedAt: a.datetime(),
|
|
1592
1601
|
})
|
|
1593
1602
|
.secondaryIndexes((index) => [
|
|
@@ -1600,6 +1609,8 @@ const definitions = {
|
|
|
1600
1609
|
index("curationStatus").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusAndUpdatedAt"),
|
|
1601
1610
|
index("curationStatusKey").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusKeyAndUpdatedAt"),
|
|
1602
1611
|
index("reviewedFeedKey").sortKeys(["updatedAt"]).queryField("listReferencesByReviewedFeedAndUpdatedAt"),
|
|
1612
|
+
index("feedStateKey").sortKeys(["predictionSortKey"]).queryField("listReferencesByFeedStateAndPrediction"),
|
|
1613
|
+
index("feedStateKey").sortKeys(["feedArrivalAt"]).queryField("listReferencesByFeedStateAndArrival"),
|
|
1603
1614
|
])
|
|
1604
1615
|
.authorization((allow) => [
|
|
1605
1616
|
allow.groups(categoryWriteGroups).to(["read"]),
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
type DelegationStore,
|
|
26
26
|
} from "../shared/curation-delegation-flow";
|
|
27
27
|
import { rejectionReasonCodeForWrittenReason } from "../../../lib/reference-policy";
|
|
28
|
+
import { referenceFeedKeys, referenceFeedStateKey } from "../../../lib/reference-feed-keys";
|
|
28
29
|
import { claimGroups } from "../../../lib/staff-groups";
|
|
29
30
|
|
|
30
31
|
type ReviewHandler = Schema["reviewSteeringProposal"]["functionHandler"];
|
|
@@ -256,6 +257,8 @@ async function reviewReferenceCuration(
|
|
|
256
257
|
curationStatusReason: note,
|
|
257
258
|
newsroomFeedKey: reference.newsroomFeedKey ?? "references",
|
|
258
259
|
reviewedFeedKey: reviewedFeedKeyForStatus(nextStatus),
|
|
260
|
+
// A vote moves the Reference between the unrated and rated feeds; its original prediction and arrival stay as they were.
|
|
261
|
+
feedStateKey: referenceFeedStateKey({ ...reference, curationStatus: nextStatus }),
|
|
259
262
|
updatedAt: now,
|
|
260
263
|
}),
|
|
261
264
|
"update Reference curation status",
|
|
@@ -642,6 +645,8 @@ async function setReferenceQualityRating(event: Parameters<ReferenceQualityHandl
|
|
|
642
645
|
curationStatusReason: note,
|
|
643
646
|
newsroomFeedKey: reference.newsroomFeedKey ?? "references",
|
|
644
647
|
reviewedFeedKey: reviewedFeedKeyForStatus(nextStatus),
|
|
648
|
+
// A vote moves the Reference between the unrated and rated feeds; its original prediction and arrival stay as they were.
|
|
649
|
+
feedStateKey: referenceFeedStateKey({ ...reference, curationStatus: nextStatus }),
|
|
645
650
|
updatedAt: now,
|
|
646
651
|
}),
|
|
647
652
|
"update Reference quality status",
|
|
@@ -818,6 +823,7 @@ async function moveReferenceCorpus(event: Parameters<MoveReferenceCorpusHandler>
|
|
|
818
823
|
client.models.Reference.update({
|
|
819
824
|
id: normalizeRequiredString(current.id, "reference.id"),
|
|
820
825
|
versionState: "superseded",
|
|
826
|
+
feedStateKey: null, // a superseded version is in no feed
|
|
821
827
|
updatedAt: now,
|
|
822
828
|
}),
|
|
823
829
|
"supersede current Reference version",
|
|
@@ -857,6 +863,19 @@ async function moveReferenceCorpus(event: Parameters<MoveReferenceCorpusHandler>
|
|
|
857
863
|
curationStatusReason: normalizeOptionalString(current.curationStatusReason),
|
|
858
864
|
newsroomFeedKey: normalizeOptionalString(current.newsroomFeedKey) ?? "references",
|
|
859
865
|
reviewedFeedKey: normalizeOptionalString(current.reviewedFeedKey) ?? reviewedFeedKeyForStatus(curationStatus),
|
|
866
|
+
predictedAcceptProbability: typeof current.predictedAcceptProbability === "number" ? current.predictedAcceptProbability : null,
|
|
867
|
+
predictionDecidedAt: normalizeOptionalString(current.predictionDecidedAt),
|
|
868
|
+
...referenceFeedKeys({
|
|
869
|
+
id: nextReferenceId,
|
|
870
|
+
corpusId: targetCorpusId,
|
|
871
|
+
curationStatus,
|
|
872
|
+
versionState: "current",
|
|
873
|
+
predictedAcceptProbability: typeof current.predictedAcceptProbability === "number" ? current.predictedAcceptProbability : null,
|
|
874
|
+
createdAt: normalizeOptionalString(current.createdAt) ?? now,
|
|
875
|
+
importedAt: normalizeOptionalString(current.importedAt),
|
|
876
|
+
retrievedAt: normalizeOptionalString(current.retrievedAt),
|
|
877
|
+
sourcePublishedAt: normalizeOptionalString(current.sourcePublishedAt),
|
|
878
|
+
}),
|
|
860
879
|
updatedAt: now,
|
|
861
880
|
}),
|
|
862
881
|
"create moved Reference version",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keys the References page reads through (docs/reference-feed-indexes.md). The Python twin is
|
|
3
|
+
* src/papyrus_content/reference_feed_keys.py; scripts/fixtures/reference-feed-key-vectors.json holds the vectors both test.
|
|
4
|
+
*
|
|
5
|
+
* `feedStateKey` is "<corpusId>#unrated" (pending) or "<corpusId>#rated" (accepted or rejected); a Reference that is not the current
|
|
6
|
+
* version, or is archived, has none and is in neither feed. `predictionSortKey` is the Cyclotron's ORIGINAL P(accept), then arrival, then
|
|
7
|
+
* id; a Reference nobody predicted sorts as 0.5. A vote changes `feedStateKey` and nothing else here.
|
|
8
|
+
*/
|
|
9
|
+
import { normalizeReferenceCurationStatus } from "./reference-policy";
|
|
10
|
+
|
|
11
|
+
export const UNDECIDED_PROBABILITY = 0.5;
|
|
12
|
+
const EPOCH = "1970-01-01T00:00:00.000Z";
|
|
13
|
+
const ARRIVAL_FIELDS = ["createdAt", "importedAt", "retrievedAt", "sourcePublishedAt", "versionCreatedAt"] as const;
|
|
14
|
+
|
|
15
|
+
export type ReferenceFeedKeyInput = {
|
|
16
|
+
id: string;
|
|
17
|
+
corpusId?: string | null;
|
|
18
|
+
curationStatus?: string | null;
|
|
19
|
+
versionState?: string | null;
|
|
20
|
+
predictedAcceptProbability?: number | null;
|
|
21
|
+
createdAt?: string | null;
|
|
22
|
+
importedAt?: string | null;
|
|
23
|
+
retrievedAt?: string | null;
|
|
24
|
+
sourcePublishedAt?: string | null;
|
|
25
|
+
versionCreatedAt?: string | null;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type ReferenceFeedKeys = {
|
|
29
|
+
feedStateKey: string | null;
|
|
30
|
+
feedArrivalAt: string;
|
|
31
|
+
predictionSortKey: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function instant(value: string | null | undefined): string | null {
|
|
35
|
+
if (!value) return null;
|
|
36
|
+
const parsed = Date.parse(value);
|
|
37
|
+
return Number.isFinite(parsed) ? new Date(parsed).toISOString() : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function referenceFeedArrivalAt(reference: ReferenceFeedKeyInput): string {
|
|
41
|
+
for (const field of ARRIVAL_FIELDS) {
|
|
42
|
+
const found = instant(reference[field]);
|
|
43
|
+
if (found) return found;
|
|
44
|
+
}
|
|
45
|
+
return EPOCH;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function referenceFeedStateKey(reference: ReferenceFeedKeyInput): string | null {
|
|
49
|
+
if (reference.versionState && reference.versionState !== "current") return null;
|
|
50
|
+
if (!reference.corpusId) return null;
|
|
51
|
+
const status = normalizeReferenceCurationStatus(reference.curationStatus);
|
|
52
|
+
if (status === "archived") return null;
|
|
53
|
+
return `${reference.corpusId}#${status === "pending" ? "unrated" : "rated"}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function predictionSortKeyFor(probability: number | null | undefined, arrival: string, referenceId: string): string {
|
|
57
|
+
const chosen = probability === null || probability === undefined ? UNDECIDED_PROBABILITY : Math.min(1, Math.max(0, probability));
|
|
58
|
+
return `${chosen.toFixed(6)}#${arrival}#${referenceId}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The three feed keys for a Reference as it stands. `probability` overrides the one stored on it. */
|
|
62
|
+
export function referenceFeedKeys(reference: ReferenceFeedKeyInput, probability?: number | null): ReferenceFeedKeys {
|
|
63
|
+
const arrival = referenceFeedArrivalAt(reference);
|
|
64
|
+
return {
|
|
65
|
+
feedStateKey: referenceFeedStateKey(reference),
|
|
66
|
+
feedArrivalAt: arrival,
|
|
67
|
+
predictionSortKey: predictionSortKeyFor(probability ?? reference.predictedAcceptProbability, arrival, reference.id),
|
|
68
|
+
};
|
|
69
|
+
}
|
package/package.json
CHANGED
|
@@ -354,6 +354,7 @@
|
|
|
354
354
|
"./lib/reference-capture": "./lib/reference-capture.ts",
|
|
355
355
|
"./lib/reference-display": "./lib/reference-display.ts",
|
|
356
356
|
"./lib/reference-feed-cache": "./lib/reference-feed-cache.ts",
|
|
357
|
+
"./lib/reference-feed-keys": "./lib/reference-feed-keys.ts",
|
|
357
358
|
"./lib/reference-feed-ranking": "./lib/reference-feed-ranking.ts",
|
|
358
359
|
"./lib/reference-policy": "./lib/reference-policy.ts",
|
|
359
360
|
"./lib/reference-preview": "./lib/reference-preview.ts",
|
|
@@ -516,7 +517,7 @@
|
|
|
516
517
|
"optional": true
|
|
517
518
|
}
|
|
518
519
|
},
|
|
519
|
-
"version": "5.43.0-next.
|
|
520
|
+
"version": "5.43.0-next.47",
|
|
520
521
|
"dependencies": {
|
|
521
522
|
"@aws-amplify/adapter-nextjs": "^1.7.3",
|
|
522
523
|
"@aws-crypto/sha256-js": "5.2.0",
|
package/routes.manifest.json
CHANGED