@littlebigbrain/create 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+ // The settings of the link workflow, the names of its parts, and the
2
+ // messages between them (general linking 8.1).
3
+ //
4
+ // One named workflow per watched class sends the class's records to its
5
+ // source shards (`<name>-<class>`, `S` shards each). A source turn finds the
6
+ // mentions of its records, decides them and sends one `decided` message to
7
+ // the things identity (`<name>-things`), which groups the decided pairs into
8
+ // things. A named workflow `<name>-reviews` watches `Mention` and its
9
+ // `same_thing_as`, so an accepted review item reaches the things identity.
10
+ import {
11
+ ENTITY_IRI_NAMESPACE,
12
+ WorkflowError,
13
+ type Schemas,
14
+ type WorkflowTriggerMessage,
15
+ } from "@littlebigbrain/client";
16
+ import type { Decision } from "./decide.js";
17
+ import type { FieldSpec } from "./sources/records.js";
18
+ import type { IdentityPair } from "./things.js";
19
+
20
+ export interface Settings {
21
+ /**
22
+ * The workflow's name. Its named workflows are `<name>-<class>` and
23
+ * `<name>-reviews`, its things identity `<name>-things`, and its review
24
+ * items have the origin `starter:<name>`.
25
+ */
26
+ name: string;
27
+ /** The role of each field of each watched class (general linking 3.2). */
28
+ fields: Record<string, Record<string, FieldSpec>>;
29
+ /** A link at or above this confidence is written; a link below it goes to a person. */
30
+ gate: number;
31
+ /**
32
+ * Orders and groups the identity items only: a person accepts every
33
+ * identity (decision 2). An identity at or above it is marked `above_gate`.
34
+ */
35
+ identityGate: number;
36
+ /**
37
+ * Whether the models may read record values (decision 1). Off: records
38
+ * link by keys and rules alone; a strong key links at confidence 1, and
39
+ * every other candidate goes to a person. On: the decision model reads the
40
+ * names, keys and context fields of the records, and the reasoning model
41
+ * reads the text fields that opt in (`{ role: "text", extract: true }`).
42
+ */
43
+ modelsReadRecords: boolean;
44
+ /** Source shards of each named workflow, 1 to 16. */
45
+ shards: number;
46
+ /** Records per trigger message, 1 to 64: one turn reads at most this many. */
47
+ sourcesPerTurn: number;
48
+ /** Characters of text one turn reads; the rest of a message continues in a new turn. */
49
+ textPerTurn: number;
50
+ /** Messages the watch keeps queued per shard. */
51
+ window: number;
52
+ }
53
+
54
+ /** The settings: edit them for your graph. These fit the samples, an invented CRM. */
55
+ export const defaults: Settings = {
56
+ name: "link",
57
+ fields: {
58
+ Person: { name: "name", email: "key:email", job_title: "context" },
59
+ Lead: {
60
+ first_name: "name:given",
61
+ last_name: "name:family",
62
+ email: "key:email",
63
+ company_name: "context",
64
+ },
65
+ Organization: { name: "name", website: "key:web" },
66
+ Deal: { name: "text" },
67
+ Note: { body: { role: "text", extract: true } },
68
+ },
69
+ gate: 0.8,
70
+ identityGate: 0.99,
71
+ modelsReadRecords: false,
72
+ shards: 4,
73
+ sourcesPerTurn: 16,
74
+ textPerTurn: 8_000,
75
+ window: 4,
76
+ };
77
+
78
+ /** The workflow type and version every part of the workflow runs. */
79
+ export const workflowType = { workflow_type: "link", version: "v1" };
80
+
81
+ /** A class name as part of a workflow name: lower case letters, digits and dashes. */
82
+ export const slug = (text: string) =>
83
+ text
84
+ .toLowerCase()
85
+ .replace(/[^a-z0-9]+/g, "-")
86
+ .replace(/^-+|-+$/g, "")
87
+ .slice(0, 40) || "class";
88
+
89
+ export const sourceWorkflowName = (settings: Settings, cls: string) =>
90
+ `${settings.name}-${slug(cls)}`;
91
+ export const reviewsWorkflowName = (settings: Settings) => `${settings.name}-reviews`;
92
+ /** The engine identity that groups the decided pairs: one per workflow (dogfooding G9). */
93
+ export const thingsId = (settings: Settings) => `${settings.name}-things`;
94
+
95
+ /** The watched class of a named workflow's messages; a name of no source workflow throws. */
96
+ export function classOf(settings: Settings, workflow: string): string {
97
+ const cls = Object.keys(settings.fields).find(
98
+ (c) => sourceWorkflowName(settings, c) === workflow,
99
+ );
100
+ if (!cls)
101
+ throw new WorkflowError(
102
+ `the message of the named workflow ${workflow} names no class of the settings (${Object.keys(settings.fields).join(", ")})`,
103
+ );
104
+ return cls;
105
+ }
106
+
107
+ /** An entity IRI from the 32-hex id a message carries. */
108
+ export const iriOf = (id: string) => (id.startsWith("http") ? id : ENTITY_IRI_NAMESPACE + id);
109
+ /** The 32-hex id of an entity IRI, for a message. */
110
+ export const idIn = (iri: string) =>
111
+ iri.startsWith(ENTITY_IRI_NAMESPACE) ? iri.slice(ENTITY_IRI_NAMESPACE.length) : iri;
112
+
113
+ /** A source turn's message: a trigger message, and the part of it a continued turn reads. */
114
+ export type SourceMessage = WorkflowTriggerMessage & { cursor?: number };
115
+
116
+ /**
117
+ * What a source turn sends the things identity. Its id is
118
+ * `decided-<shard>-<turn>` (and `.1`, `.2`, … for the parts of a large
119
+ * one), so a repeat is admitted once. Entities are named by their 32-hex
120
+ * ids.
121
+ */
122
+ export interface DecidedMessage {
123
+ type: "decided";
124
+ workflow: string;
125
+ graph: string;
126
+ as_of_commit_seq: number;
127
+ /** The shard that sent it, and its turn. */
128
+ from: string;
129
+ turn: number;
130
+ part: number;
131
+ /** The mentions the turn wrote. */
132
+ mentions: string[];
133
+ /** The mentions it retracted, and the things they were in. */
134
+ removed: string[];
135
+ things: string[];
136
+ decisions: Decision[];
137
+ identities: IdentityPair[];
138
+ /** A continued things turn: the mentions it has still to write. */
139
+ round?: number;
140
+ }
141
+
142
+ /** A message of the workflow type: a trigger message, or a decided message. */
143
+ export type LinkMessage = SourceMessage | DecidedMessage;
144
+
145
+ /** Bytes of one decided message, at most: under the engine's 32 KiB. */
146
+ export const DECIDED_BYTES = 24_000;
147
+
148
+ /**
149
+ * The calls of the workflow that the LBB activities do not make: send a
150
+ * message to the things identity, and read and supersede the review items
151
+ * it filed. `linkPorts(client)` makes them from a client; a test gives its
152
+ * own.
153
+ */
154
+ export interface LinkPorts {
155
+ send(
156
+ workflowId: string,
157
+ message: DecidedMessage,
158
+ options: { id: string; origin?: Schemas["WorkflowMessageOrigin"] },
159
+ ): Promise<unknown>;
160
+ suggestion(suggestionId: string): Promise<Schemas["OntologyChangeSuggestion"]>;
161
+ supersede(suggestionId: string, reason: string): Promise<unknown>;
162
+ }
@@ -0,0 +1,18 @@
1
+ // Create the workflow on your graph: the classes and properties it writes,
2
+ // its named workflows (one per class of the settings, and one that watches
3
+ // the accepted review items), and its things identity. Each call is safe to
4
+ // repeat.
5
+ //
6
+ // LBB_URL=... LBB_API_KEY=... LBB_GRAPH=... npm run setup
7
+ import { LbbClient, lbbActivities } from "@littlebigbrain/client";
8
+ import { setup } from "./workflow.js";
9
+
10
+ const graph = process.env.LBB_GRAPH ?? "main";
11
+ const client = new LbbClient({
12
+ baseUrl: process.env.LBB_URL ?? "http://127.0.0.1:7400",
13
+ apiKey: process.env.LBB_API_KEY,
14
+ graph,
15
+ });
16
+ const names = await setup(client, lbbActivities(client));
17
+ console.log(`Created ${names.join(", ")} on the graph ${graph}.`);
18
+ console.log(`Serve them with LBB_GRAPH=${graph} npm start.`);