@memberjunction/ai-recommendations 5.0.0 → 5.1.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.
Files changed (2) hide show
  1. package/README.md +376 -0
  2. package/package.json +5 -5
package/README.md ADDED
@@ -0,0 +1,376 @@
1
+ # @memberjunction/ai-recommendations
2
+
3
+ A provider-based recommendation engine for MemberJunction. Manages recommendation runs, delegates to pluggable providers via the class factory, and tracks results through Recommendation, Recommendation Run, and Recommendation Item entities.
4
+
5
+ ## Architecture
6
+
7
+ ```mermaid
8
+ graph TD
9
+ subgraph Engine["@memberjunction/ai-recommendations"]
10
+ REB["RecommendationEngineBase<br/>(singleton BaseEngine)"]
11
+ RPB["RecommendationProviderBase<br/>(abstract)"]
12
+ RR["RecommendationRequest&lt;T&gt;"]
13
+ RRES["RecommendationResult"]
14
+ end
15
+
16
+ subgraph Providers["Registered Providers"]
17
+ P1["Provider A"]
18
+ P2["Provider B"]
19
+ end
20
+
21
+ subgraph MJEntities["MemberJunction Entities"]
22
+ RP["Recommendation Providers"]
23
+ RUN["Recommendation Runs"]
24
+ REC["Recommendations"]
25
+ RI["Recommendation Items"]
26
+ LIST["Lists / List Details"]
27
+ end
28
+
29
+ subgraph MJCore["MemberJunction Core"]
30
+ BE["BaseEngine"]
31
+ CF["ClassFactory"]
32
+ MD["Metadata"]
33
+ end
34
+
35
+ REB -->|extends| BE
36
+ REB -->|discovers| CF
37
+ CF -->|creates| P1
38
+ CF -->|creates| P2
39
+ P1 -->|extends| RPB
40
+ P2 -->|extends| RPB
41
+ REB --> RP
42
+ REB --> RUN
43
+ RPB --> REC
44
+ RPB --> RI
45
+ REB --> LIST
46
+
47
+ style Engine fill:#2d6a9f,stroke:#1a4971,color:#fff
48
+ style Providers fill:#2d8659,stroke:#1a5c3a,color:#fff
49
+ style MJEntities fill:#b8762f,stroke:#8a5722,color:#fff
50
+ style MJCore fill:#7c5295,stroke:#563a6b,color:#fff
51
+ ```
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ npm install @memberjunction/ai-recommendations
57
+ ```
58
+
59
+ ## Overview
60
+
61
+ This package provides the framework for running recommendations in MemberJunction. It follows the engine/provider pattern used throughout the platform:
62
+
63
+ 1. **RecommendationEngineBase** -- a singleton engine (extending `BaseEngine`) that loads provider metadata, selects a provider, creates Recommendation Run tracking records, and delegates the actual recommendation logic
64
+ 2. **RecommendationProviderBase** -- an abstract class that concrete providers implement to generate recommendations for each source record
65
+ 3. **RecommendationRequest/RecommendationResult** -- typed request and response objects that flow through the pipeline
66
+
67
+ Providers are discovered at runtime through MemberJunction's `ClassFactory` using `@RegisterClass(RecommendationProviderBase, 'ProviderName')`.
68
+
69
+ ## Recommendation Flow
70
+
71
+ ```mermaid
72
+ sequenceDiagram
73
+ participant Caller
74
+ participant Engine as RecommendationEngineBase
75
+ participant CF as ClassFactory
76
+ participant Provider as RecommendationProvider
77
+ participant DB as MJ Database
78
+
79
+ Caller->>Engine: Recommend(request)
80
+ Engine->>Engine: TryThrowIfNotLoaded()
81
+
82
+ alt Provider specified
83
+ Engine->>Engine: Use request.Provider
84
+ else No provider
85
+ Engine->>Engine: Use first available
86
+ end
87
+
88
+ Engine->>Engine: GetRecommendationEntities(request)
89
+
90
+ alt From List
91
+ Engine->>DB: Load List + List Details
92
+ Engine->>DB: Load entity records by IDs
93
+ else From EntityAndRecordsInfo
94
+ Engine->>DB: Load records by entity name + IDs
95
+ else Pre-built
96
+ Engine->>Engine: Validate Recommendations array
97
+ end
98
+
99
+ Engine->>DB: Create Recommendation Run (Status: In Progress)
100
+
101
+ opt CreateErrorList = true
102
+ Engine->>DB: Create error tracking List
103
+ end
104
+
105
+ Engine->>CF: CreateInstance(provider.Name)
106
+ CF-->>Engine: Provider instance
107
+
108
+ Engine->>Provider: Recommend(request)
109
+
110
+ loop For each recommendation
111
+ Provider->>Provider: Call external API
112
+ Provider->>DB: SaveRecommendation + Items
113
+ end
114
+
115
+ Provider-->>Engine: RecommendationResult
116
+
117
+ Engine->>DB: Update Run (Completed/Error)
118
+ Engine-->>Caller: RecommendationResult
119
+ ```
120
+
121
+ ## Core Components
122
+
123
+ ### RecommendationEngineBase
124
+
125
+ A singleton engine that manages the recommendation lifecycle.
126
+
127
+ ```typescript
128
+ import { RecommendationEngineBase } from '@memberjunction/ai-recommendations';
129
+
130
+ // Access the singleton
131
+ const engine = RecommendationEngineBase.Instance;
132
+
133
+ // Initialize (loads Recommendation Providers metadata)
134
+ await engine.Config(false, contextUser);
135
+
136
+ // Run recommendations
137
+ const result = await engine.Recommend(request);
138
+ ```
139
+
140
+ **Key properties and methods:**
141
+
142
+ | Member | Description |
143
+ |---|---|
144
+ | `Instance` | Static getter for the singleton instance |
145
+ | `RecommendationProviders` | Array of `RecommendationProviderEntity` loaded from metadata |
146
+ | `Config(forceRefresh?, contextUser?, provider?)` | Loads provider metadata into cache |
147
+ | `Recommend<T>(request)` | Runs the full recommendation pipeline |
148
+
149
+ ### RecommendationProviderBase
150
+
151
+ Abstract base class for implementing recommendation providers.
152
+
153
+ ```mermaid
154
+ classDiagram
155
+ class RecommendationProviderBase {
156
+ <<abstract>>
157
+ -_md : Metadata
158
+ -_ContextUser : UserInfo
159
+ +ContextUser : UserInfo
160
+ +Recommend(request)* RecommendationResult
161
+ #SaveRecommendation(rec, runID, items) boolean
162
+ }
163
+
164
+ class ConcreteProvider {
165
+ +Recommend(request) RecommendationResult
166
+ }
167
+
168
+ RecommendationProviderBase <|-- ConcreteProvider
169
+
170
+ style RecommendationProviderBase fill:#2d6a9f,stroke:#1a4971,color:#fff
171
+ style ConcreteProvider fill:#2d8659,stroke:#1a5c3a,color:#fff
172
+ ```
173
+
174
+ The `SaveRecommendation` helper method handles:
175
+ 1. Setting the `RecommendationRunID` on the recommendation entity
176
+ 2. Saving the recommendation record
177
+ 3. Linking and saving all `RecommendationItemEntity` records
178
+
179
+ ### RecommendationRequest\<T\>
180
+
181
+ The request object supports three ways to specify source records:
182
+
183
+ ```mermaid
184
+ graph TD
185
+ RR["RecommendationRequest"]
186
+ OPT1["Recommendations[]<br/>Pre-built entities"]
187
+ OPT2["EntityAndRecordsInfo<br/>Entity name + Record IDs"]
188
+ OPT3["ListID<br/>MJ List reference"]
189
+
190
+ RR --> OPT1
191
+ RR --> OPT2
192
+ RR --> OPT3
193
+
194
+ style RR fill:#2d6a9f,stroke:#1a4971,color:#fff
195
+ style OPT1 fill:#2d8659,stroke:#1a5c3a,color:#fff
196
+ style OPT2 fill:#2d8659,stroke:#1a5c3a,color:#fff
197
+ style OPT3 fill:#2d8659,stroke:#1a5c3a,color:#fff
198
+ ```
199
+
200
+ | Field | Type | Description |
201
+ |---|---|---|
202
+ | `Recommendations` | `RecommendationEntity[]` | Pre-built unsaved recommendation entities |
203
+ | `EntityAndRecordsInfo` | `{ EntityName, RecordIDs }` | Entity name and array of record IDs to process |
204
+ | `ListID` | `string` | ID of a MJ List whose details become the source records |
205
+ | `Provider` | `RecommendationProviderEntity` | Specific provider to use (defaults to first available) |
206
+ | `CurrentUser` | `UserInfo` | User context |
207
+ | `Options` | `T` | Generic additional options passed to the provider |
208
+ | `CreateErrorList` | `boolean` | Whether to create an error tracking list |
209
+ | `RunID` | `string` | Set automatically by the engine |
210
+ | `ErrorListID` | `string` | Set automatically if error list is created |
211
+
212
+ ### RecommendationResult
213
+
214
+ ```typescript
215
+ class RecommendationResult {
216
+ Request: RecommendationRequest;
217
+ RecommendationRun?: RecommendationRunEntity;
218
+ RecommendationItems?: RecommendationItemEntity[];
219
+ Success: boolean;
220
+ ErrorMessage: string;
221
+
222
+ AppendWarning(message: string): void; // Adds warning without setting Success=false
223
+ AppendError(message: string): void; // Adds error and sets Success=false
224
+ GetErrorMessages(): string[]; // Splits ErrorMessage into array
225
+ }
226
+ ```
227
+
228
+ ## Usage
229
+
230
+ ### Running Recommendations from a List
231
+
232
+ ```typescript
233
+ import { RecommendationEngineBase } from '@memberjunction/ai-recommendations';
234
+ import { RecommendationRequest } from '@memberjunction/ai-recommendations';
235
+
236
+ const engine = RecommendationEngineBase.Instance;
237
+ await engine.Config(false, contextUser);
238
+
239
+ const request = new RecommendationRequest();
240
+ request.ListID = 'list-uuid';
241
+ request.CurrentUser = contextUser;
242
+ request.CreateErrorList = true;
243
+
244
+ const result = await engine.Recommend(request);
245
+
246
+ if (result.Success) {
247
+ console.log(`Generated ${result.RecommendationItems?.length ?? 0} items`);
248
+ } else {
249
+ console.error(result.ErrorMessage);
250
+ }
251
+ ```
252
+
253
+ ### Running Recommendations by Entity and Record IDs
254
+
255
+ ```typescript
256
+ const request = new RecommendationRequest();
257
+ request.EntityAndRecordsInfo = {
258
+ EntityName: 'Products',
259
+ RecordIDs: ['id-1', 'id-2', 'id-3']
260
+ };
261
+ request.CurrentUser = contextUser;
262
+
263
+ const result = await engine.Recommend(request);
264
+ ```
265
+
266
+ ### Implementing a Provider
267
+
268
+ ```typescript
269
+ import { RecommendationProviderBase } from '@memberjunction/ai-recommendations';
270
+ import { RecommendationRequest, RecommendationResult } from '@memberjunction/ai-recommendations';
271
+ import { RegisterClass } from '@memberjunction/global';
272
+ import { Metadata } from '@memberjunction/core';
273
+ import { RecommendationItemEntity } from '@memberjunction/core-entities';
274
+
275
+ @RegisterClass(RecommendationProviderBase, 'My Recommendation Provider')
276
+ export class MyProvider extends RecommendationProviderBase {
277
+ async Recommend(request: RecommendationRequest): Promise<RecommendationResult> {
278
+ const result = new RecommendationResult(request);
279
+ const md = new Metadata();
280
+
281
+ for (const rec of request.Recommendations) {
282
+ // Call your recommendation API/algorithm
283
+ const suggestions = await this.getSuggestions(rec.SourceEntityRecordID);
284
+
285
+ const items: RecommendationItemEntity[] = [];
286
+ for (const suggestion of suggestions) {
287
+ const item = await md.GetEntityObject<RecommendationItemEntity>(
288
+ 'Recommendation Items', request.CurrentUser
289
+ );
290
+ item.NewRecord();
291
+ item.DestinationEntityID = suggestion.entityID;
292
+ item.DestinationEntityRecordID = suggestion.recordID;
293
+ item.MatchProbability = suggestion.score;
294
+ items.push(item);
295
+ }
296
+
297
+ await this.SaveRecommendation(rec, request.RunID, items);
298
+ }
299
+
300
+ return result;
301
+ }
302
+
303
+ private async getSuggestions(recordID: string): Promise<Suggestion[]> {
304
+ // Your recommendation logic here
305
+ return [];
306
+ }
307
+ }
308
+ ```
309
+
310
+ ## Database Entities
311
+
312
+ ```mermaid
313
+ erDiagram
314
+ RECOMMENDATION_PROVIDERS {
315
+ string ID PK
316
+ string Name
317
+ string Description
318
+ }
319
+
320
+ RECOMMENDATION_RUNS {
321
+ string ID PK
322
+ string RecommendationProviderID FK
323
+ string RunByUserID FK
324
+ datetime StartDate
325
+ string Status
326
+ string Description
327
+ }
328
+
329
+ RECOMMENDATIONS {
330
+ string ID PK
331
+ string RecommendationRunID FK
332
+ string SourceEntityID FK
333
+ string SourceEntityRecordID
334
+ }
335
+
336
+ RECOMMENDATION_ITEMS {
337
+ string ID PK
338
+ string RecommendationID FK
339
+ string DestinationEntityID FK
340
+ string DestinationEntityRecordID
341
+ float MatchProbability
342
+ }
343
+
344
+ LISTS {
345
+ string ID PK
346
+ string Name
347
+ string EntityID FK
348
+ string UserID FK
349
+ }
350
+
351
+ RECOMMENDATION_PROVIDERS ||--o{ RECOMMENDATION_RUNS : has
352
+ RECOMMENDATION_RUNS ||--o{ RECOMMENDATIONS : contains
353
+ RECOMMENDATIONS ||--o{ RECOMMENDATION_ITEMS : produces
354
+ ```
355
+
356
+ ## Dependencies
357
+
358
+ | Package | Purpose |
359
+ |---|---|
360
+ | `@memberjunction/core` | `BaseEngine`, `Metadata`, `RunView`, `UserInfo`, `LogStatus` |
361
+ | `@memberjunction/core-entities` | `RecommendationEntity`, `RecommendationRunEntity`, `RecommendationItemEntity`, `RecommendationProviderEntity`, `ListEntity` |
362
+ | `@memberjunction/global` | `MJGlobal` class factory for provider discovery |
363
+
364
+ ## Development
365
+
366
+ ```bash
367
+ # Build
368
+ npm run build
369
+
370
+ # Development mode
371
+ npm run start
372
+ ```
373
+
374
+ ## License
375
+
376
+ ISC
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/ai-recommendations",
3
3
  "type": "module",
4
- "version": "5.0.0",
4
+ "version": "5.1.0",
5
5
  "description": "MemberJunction Recommendations Engine",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -21,10 +21,10 @@
21
21
  "typescript": "^5.9.3"
22
22
  },
23
23
  "dependencies": {
24
- "@memberjunction/global": "5.0.0",
25
- "@memberjunction/core": "5.0.0",
26
- "@memberjunction/core-entities": "5.0.0",
27
- "@memberjunction/ai": "5.0.0"
24
+ "@memberjunction/global": "5.1.0",
25
+ "@memberjunction/core": "5.1.0",
26
+ "@memberjunction/core-entities": "5.1.0",
27
+ "@memberjunction/ai": "5.1.0"
28
28
  },
29
29
  "repository": {
30
30
  "type": "git",