@memberjunction/aiengine 3.4.0 → 4.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.
- package/README.md +123 -666
- package/dist/AIEngine.d.ts +243 -13
- package/dist/AIEngine.d.ts.map +1 -1
- package/dist/AIEngine.js +406 -154
- package/dist/AIEngine.js.map +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +8 -24
- package/dist/index.js.map +1 -1
- package/dist/services/ActionEmbeddingService.d.ts +72 -1
- package/dist/services/ActionEmbeddingService.d.ts.map +1 -1
- package/dist/services/ActionEmbeddingService.js +88 -12
- package/dist/services/ActionEmbeddingService.js.map +1 -1
- package/dist/services/AgentEmbeddingService.d.ts +70 -1
- package/dist/services/AgentEmbeddingService.d.ts.map +1 -1
- package/dist/services/AgentEmbeddingService.js +87 -13
- package/dist/services/AgentEmbeddingService.js.map +1 -1
- package/dist/services/ConversationAttachmentService.d.ts +126 -0
- package/dist/services/ConversationAttachmentService.d.ts.map +1 -1
- package/dist/services/ConversationAttachmentService.js +180 -25
- package/dist/services/ConversationAttachmentService.js.map +1 -1
- package/dist/types/ActionMatchResult.d.ts +48 -0
- package/dist/types/ActionMatchResult.d.ts.map +1 -1
- package/dist/types/ActionMatchResult.js +1 -2
- package/dist/types/AgentMatchResult.d.ts +52 -0
- package/dist/types/AgentMatchResult.d.ts.map +1 -1
- package/dist/types/AgentMatchResult.js +1 -2
- package/dist/types/ExampleMatchResult.d.ts +8 -0
- package/dist/types/ExampleMatchResult.d.ts.map +1 -1
- package/dist/types/ExampleMatchResult.js +1 -2
- package/dist/types/NoteMatchResult.d.ts +8 -0
- package/dist/types/NoteMatchResult.d.ts.map +1 -1
- package/dist/types/NoteMatchResult.js +1 -2
- package/package.json +16 -15
package/dist/AIEngine.js
CHANGED
|
@@ -1,56 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.LoadAIEngine = exports.AIEngine = exports.EntityAIActionParams = exports.AIActionParams = void 0;
|
|
27
|
-
const ai_1 = require("@memberjunction/ai");
|
|
28
|
-
const core_1 = require("@memberjunction/core");
|
|
29
|
-
const global_1 = require("@memberjunction/global");
|
|
30
|
-
const ai_engine_base_1 = require("@memberjunction/ai-engine-base");
|
|
31
|
-
const ai_vectors_memory_1 = require("@memberjunction/ai-vectors-memory");
|
|
32
|
-
const AgentEmbeddingService_1 = require("./services/AgentEmbeddingService");
|
|
33
|
-
const ActionEmbeddingService_1 = require("./services/ActionEmbeddingService");
|
|
34
|
-
const actions_base_1 = require("@memberjunction/actions-base");
|
|
35
|
-
class AIActionParams {
|
|
1
|
+
import { BaseLLM, BaseModel, ChatParams, ChatMessageRole, GetAIAPIKey, BaseEmbeddings } from "@memberjunction/ai";
|
|
2
|
+
import { LogError, Metadata } from "@memberjunction/core";
|
|
3
|
+
import { BaseSingleton, MJGlobal } from "@memberjunction/global";
|
|
4
|
+
import { AIEngineBase } from "@memberjunction/ai-engine-base";
|
|
5
|
+
import { SimpleVectorService } from "@memberjunction/ai-vectors-memory";
|
|
6
|
+
import { AgentEmbeddingService } from "./services/AgentEmbeddingService.js";
|
|
7
|
+
import { ActionEmbeddingService } from "./services/ActionEmbeddingService.js";
|
|
8
|
+
import { ActionEngineBase } from "@memberjunction/actions-base";
|
|
9
|
+
/**
|
|
10
|
+
* @deprecated AI Actions are deprecated. Use AIPromptRunner with the new AI Prompt system instead.
|
|
11
|
+
*/
|
|
12
|
+
export class AIActionParams {
|
|
36
13
|
}
|
|
37
|
-
|
|
38
|
-
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Entity AI Actions are deprecated. Use AIPromptRunner with the new AI Prompt system instead.
|
|
16
|
+
*/
|
|
17
|
+
export class EntityAIActionParams extends AIActionParams {
|
|
39
18
|
}
|
|
40
|
-
|
|
41
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Server-side AI Engine that wraps AIEngineBase and adds server-only capabilities.
|
|
21
|
+
*
|
|
22
|
+
* This class uses composition (containment) rather than inheritance to avoid duplicate
|
|
23
|
+
* data loading. It delegates all base functionality to AIEngineBase.Instance while
|
|
24
|
+
* adding server-specific features like embeddings, vector search, and LLM execution.
|
|
25
|
+
*
|
|
26
|
+
* @description ONLY USE ON SERVER-SIDE. For metadata only, use the AIEngineBase class which can be used anywhere.
|
|
27
|
+
*/
|
|
28
|
+
export class AIEngine extends BaseSingleton {
|
|
42
29
|
constructor() {
|
|
43
30
|
super(...arguments);
|
|
44
31
|
this.EmbeddingModelTypeName = 'Embeddings';
|
|
45
32
|
this.LocalEmbeddingModelVendorName = 'LocalEmbeddings';
|
|
33
|
+
// Vector service for agent embeddings - initialized during Config
|
|
46
34
|
this._agentVectorService = null;
|
|
35
|
+
// Vector service for action embeddings - initialized during Config
|
|
47
36
|
this._actionVectorService = null;
|
|
37
|
+
// Vector service for note embeddings - initialized during Config
|
|
48
38
|
this._noteVectorService = null;
|
|
39
|
+
// Vector service for example embeddings - initialized during Config
|
|
49
40
|
this._exampleVectorService = null;
|
|
41
|
+
// Actions loaded from database
|
|
50
42
|
this._actions = [];
|
|
43
|
+
// Embedding caches to track which items have embeddings generated
|
|
51
44
|
this._agentEmbeddingsCache = new Map();
|
|
52
45
|
this._actionEmbeddingsCache = new Map();
|
|
53
46
|
this._embeddingsGenerated = false;
|
|
47
|
+
// Loading state management
|
|
54
48
|
this._loaded = false;
|
|
55
49
|
this._loading = false;
|
|
56
50
|
this._loadingPromise = null;
|
|
@@ -58,12 +52,19 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
58
52
|
static get Instance() {
|
|
59
53
|
return super.getInstance();
|
|
60
54
|
}
|
|
55
|
+
// ========================================================================
|
|
56
|
+
// Delegated Properties from AIEngineBase
|
|
57
|
+
// All base metadata is accessed through AIEngineBase.Instance
|
|
58
|
+
// ========================================================================
|
|
59
|
+
/** Access to the underlying AIEngineBase instance */
|
|
61
60
|
get Base() {
|
|
62
|
-
return
|
|
61
|
+
return AIEngineBase.Instance;
|
|
63
62
|
}
|
|
63
|
+
/** Returns true if both the base engine and server capabilities are loaded */
|
|
64
64
|
get Loaded() {
|
|
65
65
|
return this._loaded && this.Base.Loaded;
|
|
66
66
|
}
|
|
67
|
+
// Delegate all AIEngineBase public getters
|
|
67
68
|
get Agents() { return this.Base.Agents; }
|
|
68
69
|
get AgentRelationships() { return this.Base.AgentRelationships; }
|
|
69
70
|
get AgentTypes() { return this.Base.AgentTypes; }
|
|
@@ -102,11 +103,15 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
102
103
|
get AgentSteps() { return this.Base.AgentSteps; }
|
|
103
104
|
get AgentStepPaths() { return this.Base.AgentStepPaths; }
|
|
104
105
|
get ModelActions() { return this.Base.ModelActions; }
|
|
106
|
+
/** @deprecated Use the new Action system instead */
|
|
105
107
|
get Actions() { return this.Base.Actions; }
|
|
108
|
+
/** @deprecated Use the new Action system instead */
|
|
106
109
|
get EntityAIActions() { return this.Base.EntityAIActions; }
|
|
110
|
+
// Modality getters - delegated from AIEngineBase
|
|
107
111
|
get Modalities() { return this.Base.Modalities; }
|
|
108
112
|
get AgentModalities() { return this.Base.AgentModalities; }
|
|
109
113
|
get ModelModalities() { return this.Base.ModelModalities; }
|
|
114
|
+
// Modality helper methods - delegated from AIEngineBase
|
|
110
115
|
GetModalityByName(name) {
|
|
111
116
|
return this.Base.GetModalityByName(name);
|
|
112
117
|
}
|
|
@@ -128,6 +133,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
128
133
|
GetAgentSupportedInputModalities(agentId) {
|
|
129
134
|
return this.Base.GetAgentSupportedInputModalities(agentId);
|
|
130
135
|
}
|
|
136
|
+
// Delegate AIEngineBase public methods
|
|
131
137
|
async GetHighestPowerModel(vendorName, modelType, contextUser) {
|
|
132
138
|
return this.Base.GetHighestPowerModel(vendorName, modelType, contextUser);
|
|
133
139
|
}
|
|
@@ -161,9 +167,26 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
161
167
|
GetConfigurationParam(configurationId, paramName) {
|
|
162
168
|
return this.Base.GetConfigurationParam(configurationId, paramName);
|
|
163
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Returns the inheritance chain for a configuration, starting with the specified
|
|
172
|
+
* configuration and walking up through parent configurations to the root.
|
|
173
|
+
* Delegates to AIEngineBase.GetConfigurationChain.
|
|
174
|
+
*
|
|
175
|
+
* @param configurationId - The ID of the configuration to get the chain for
|
|
176
|
+
* @returns Array of AIConfigurationEntity objects representing the inheritance chain
|
|
177
|
+
* @throws Error if a circular reference is detected in the configuration hierarchy
|
|
178
|
+
*/
|
|
164
179
|
GetConfigurationChain(configurationId) {
|
|
165
180
|
return this.Base.GetConfigurationChain(configurationId);
|
|
166
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Returns all configuration parameters for a configuration, including inherited
|
|
184
|
+
* parameters from parent configurations. Child parameters override parent parameters.
|
|
185
|
+
* Delegates to AIEngineBase.GetConfigurationParamsWithInheritance.
|
|
186
|
+
*
|
|
187
|
+
* @param configurationId - The ID of the configuration to get parameters for
|
|
188
|
+
* @returns Array of AIConfigurationParamEntity objects, with child overrides applied
|
|
189
|
+
*/
|
|
167
190
|
GetConfigurationParamsWithInheritance(configurationId) {
|
|
168
191
|
return this.Base.GetConfigurationParamsWithInheritance(configurationId);
|
|
169
192
|
}
|
|
@@ -206,22 +229,56 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
206
229
|
async RefreshAgentPermissionsCache(agentId, user) {
|
|
207
230
|
return this.Base.RefreshAgentPermissionsCache(agentId, user);
|
|
208
231
|
}
|
|
232
|
+
// ========================================================================
|
|
233
|
+
// Server-Only Properties
|
|
234
|
+
// ========================================================================
|
|
235
|
+
/**
|
|
236
|
+
* Get the agent vector service for semantic search.
|
|
237
|
+
* Initialized during Config - will be null before AIEngine.Config() completes.
|
|
238
|
+
*/
|
|
209
239
|
get AgentVectorService() {
|
|
210
240
|
return this._agentVectorService;
|
|
211
241
|
}
|
|
242
|
+
/**
|
|
243
|
+
* Get the action vector service for semantic search.
|
|
244
|
+
* Initialized during Config - will be null before AIEngine.Config() completes.
|
|
245
|
+
*/
|
|
212
246
|
get ActionVectorService() {
|
|
213
247
|
return this._actionVectorService;
|
|
214
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* Get all available actions loaded from the database.
|
|
251
|
+
* Loaded during Config() - will be empty before AIEngine.Config() completes.
|
|
252
|
+
* NOTE: This returns ActionEntity (MJ Action system), not the deprecated AIActionEntity.
|
|
253
|
+
* For deprecated AI Actions, see the inherited Actions property.
|
|
254
|
+
*/
|
|
215
255
|
get SystemActions() {
|
|
216
256
|
return this._actions;
|
|
217
257
|
}
|
|
258
|
+
// ========================================================================
|
|
259
|
+
// Config - Main Entry Point
|
|
260
|
+
// ========================================================================
|
|
261
|
+
/**
|
|
262
|
+
* Configures the AIEngine by first ensuring AIEngineBase is configured,
|
|
263
|
+
* then loading server-specific capabilities (embeddings, actions, etc.).
|
|
264
|
+
*
|
|
265
|
+
* This method is safe to call from multiple places concurrently - it will
|
|
266
|
+
* return the same promise to all callers during loading.
|
|
267
|
+
*
|
|
268
|
+
* @param forceRefresh - If true, forces a full reload even if already loaded
|
|
269
|
+
* @param contextUser - User context for server-side operations (required)
|
|
270
|
+
* @param provider - Optional metadata provider override
|
|
271
|
+
*/
|
|
218
272
|
async Config(forceRefresh, contextUser, provider) {
|
|
273
|
+
// If already loaded and not forcing refresh, return immediately
|
|
219
274
|
if (this._loaded && !forceRefresh) {
|
|
220
275
|
return;
|
|
221
276
|
}
|
|
277
|
+
// If currently loading, return the existing promise so all callers wait together
|
|
222
278
|
if (this._loading && this._loadingPromise) {
|
|
223
279
|
return this._loadingPromise;
|
|
224
280
|
}
|
|
281
|
+
// Start loading
|
|
225
282
|
this._loading = true;
|
|
226
283
|
this._loadingPromise = this.innerLoad(forceRefresh, contextUser, provider);
|
|
227
284
|
try {
|
|
@@ -232,110 +289,187 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
232
289
|
this._loadingPromise = null;
|
|
233
290
|
}
|
|
234
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Internal loading logic - separated for clean promise management
|
|
294
|
+
*/
|
|
235
295
|
async innerLoad(forceRefresh, contextUser, provider) {
|
|
236
296
|
try {
|
|
237
297
|
this._contextUser = contextUser;
|
|
238
|
-
|
|
239
|
-
|
|
298
|
+
// First, ensure AIEngineBase is configured
|
|
299
|
+
// This is where all the metadata loading happens - we share that data
|
|
300
|
+
await AIEngineBase.Instance.Config(forceRefresh ?? false, contextUser, provider);
|
|
301
|
+
// Now load server-specific capabilities
|
|
302
|
+
await this.RefreshServerSpecificMetadata(contextUser);
|
|
240
303
|
this._loaded = true;
|
|
241
304
|
}
|
|
242
305
|
catch (error) {
|
|
243
|
-
|
|
306
|
+
LogError(error);
|
|
244
307
|
throw error;
|
|
245
308
|
}
|
|
246
309
|
}
|
|
247
|
-
|
|
248
|
-
|
|
310
|
+
/**
|
|
311
|
+
* Refreshes the server metadata including active actions.
|
|
312
|
+
* Refreshes the embeddings in the engine's vector service for
|
|
313
|
+
* - Agents (dynamic recalc of embeddings)
|
|
314
|
+
* - Actions (dynamic recalc of embeddings)
|
|
315
|
+
* - Notes (parsed from DB)
|
|
316
|
+
* - Examples (parsed from DB)
|
|
317
|
+
*
|
|
318
|
+
* If you only need to refresh specific elements noted above, call the individual methods:
|
|
319
|
+
* - RefreshActions (refreshes just the server side action metadata - e.g. 'Active' Actions)
|
|
320
|
+
* - RefreshActionEmbeddings (dynamic recalc of embedings from stored data)
|
|
321
|
+
* - RefreshAgentEmbeddings (dynamic recalc of embeddings from stored data)
|
|
322
|
+
* - RefreshNoteEmbeddings
|
|
323
|
+
* - RefreshExampleEmbeddings
|
|
324
|
+
*/
|
|
325
|
+
async RefreshServerSpecificMetadata(contextUser) {
|
|
326
|
+
// Load actions from the Action system
|
|
327
|
+
await this.RefreshActions(contextUser);
|
|
328
|
+
// Load all embeddings in parallel since they are independent
|
|
249
329
|
await Promise.all([
|
|
250
|
-
this.
|
|
251
|
-
this.
|
|
252
|
-
this.
|
|
253
|
-
this.
|
|
330
|
+
this.RefreshAgentEmbeddings(),
|
|
331
|
+
this.RefreshActionEmbeddings(),
|
|
332
|
+
this.RefreshNoteEmbeddings(contextUser),
|
|
333
|
+
this.RefreshExampleEmbeddings(contextUser)
|
|
254
334
|
]);
|
|
255
335
|
this._embeddingsGenerated = true;
|
|
256
336
|
}
|
|
337
|
+
// ========================================================================
|
|
338
|
+
// Embedding Generation Methods
|
|
339
|
+
// ========================================================================
|
|
340
|
+
/**
|
|
341
|
+
* Force regeneration of all embeddings for agents and actions.
|
|
342
|
+
*
|
|
343
|
+
* Use this method when:
|
|
344
|
+
* - Switching to a different embedding model
|
|
345
|
+
* - Agent or Action descriptions have been significantly updated
|
|
346
|
+
* - You want to ensure embeddings are up-to-date after bulk changes
|
|
347
|
+
* - Troubleshooting embedding-related issues
|
|
348
|
+
*
|
|
349
|
+
* Note: This is an expensive operation and should not be called frequently.
|
|
350
|
+
* Normal auto-refresh operations will NOT regenerate embeddings to avoid performance issues.
|
|
351
|
+
*
|
|
352
|
+
* @param contextUser - User context for database operations (required on server-side)
|
|
353
|
+
*/
|
|
257
354
|
async RegenerateEmbeddings(contextUser) {
|
|
258
355
|
try {
|
|
356
|
+
// Clear the caches
|
|
259
357
|
this._agentEmbeddingsCache.clear();
|
|
260
358
|
this._actionEmbeddingsCache.clear();
|
|
261
359
|
this._embeddingsGenerated = false;
|
|
360
|
+
// Clear the vector services
|
|
262
361
|
this._agentVectorService = null;
|
|
263
362
|
this._actionVectorService = null;
|
|
264
|
-
|
|
265
|
-
await this.
|
|
266
|
-
await this.
|
|
363
|
+
// Reload actions and regenerate embeddings
|
|
364
|
+
await this.RefreshActions(contextUser);
|
|
365
|
+
await this.RefreshAgentEmbeddings();
|
|
366
|
+
await this.RefreshActionEmbeddings();
|
|
267
367
|
this._embeddingsGenerated = true;
|
|
268
368
|
}
|
|
269
369
|
catch (error) {
|
|
270
|
-
|
|
370
|
+
LogError('AIEngine: Failed to regenerate embeddings', undefined, error instanceof Error ? error : undefined);
|
|
271
371
|
throw error;
|
|
272
372
|
}
|
|
273
373
|
}
|
|
274
|
-
|
|
374
|
+
/**
|
|
375
|
+
* Refreshes Agent embeddings - agents are pre-loaded at this point, but we need
|
|
376
|
+
* to generate, dynamically, embeddings from the text stored in the agent. This is not a
|
|
377
|
+
* cheap operation, use it sparingly.
|
|
378
|
+
*/
|
|
379
|
+
async RefreshAgentEmbeddings() {
|
|
275
380
|
try {
|
|
276
|
-
|
|
381
|
+
// Use agents already loaded by base class
|
|
382
|
+
const agents = this.Agents; // Delegates to AIEngineBase
|
|
277
383
|
if (!agents || agents.length === 0) {
|
|
278
384
|
return;
|
|
279
385
|
}
|
|
386
|
+
// Filter out restricted agents - they should not be discoverable
|
|
280
387
|
const nonRestrictedAgents = agents.filter(agent => !agent.IsRestricted);
|
|
388
|
+
// Filter to only agents that don't have embeddings yet
|
|
281
389
|
const agentsNeedingEmbeddings = nonRestrictedAgents.filter(agent => !this._agentEmbeddingsCache.has(agent.ID));
|
|
282
390
|
if (agentsNeedingEmbeddings.length === 0) {
|
|
283
391
|
return;
|
|
284
392
|
}
|
|
285
|
-
|
|
393
|
+
// Generate embeddings using static utility method
|
|
394
|
+
const entries = await AgentEmbeddingService.GenerateAgentEmbeddings(agentsNeedingEmbeddings, (text) => this.EmbedTextLocal(text));
|
|
395
|
+
// Mark these agents as having embeddings
|
|
286
396
|
for (const agent of agentsNeedingEmbeddings) {
|
|
287
397
|
this._agentEmbeddingsCache.set(agent.ID, true);
|
|
288
398
|
}
|
|
399
|
+
// Load into vector service (create if needed, or add to existing)
|
|
289
400
|
if (!this._agentVectorService) {
|
|
290
|
-
this._agentVectorService = new
|
|
401
|
+
this._agentVectorService = new SimpleVectorService();
|
|
291
402
|
}
|
|
292
403
|
this._agentVectorService.LoadVectors(entries);
|
|
293
404
|
}
|
|
294
405
|
catch (error) {
|
|
295
|
-
|
|
406
|
+
LogError(`AIEngine: Failed to load agent embeddings: ${error instanceof Error ? error.message : String(error)}`);
|
|
407
|
+
// Don't throw - allow AIEngine to continue loading even if embeddings fail
|
|
296
408
|
}
|
|
297
409
|
}
|
|
298
|
-
|
|
410
|
+
/**
|
|
411
|
+
* Loads Active actions from the base engine (contained within this class). Does **not** refresh from the database, simply
|
|
412
|
+
* pulls the latest `Active` actions from the base class into its server side only array.
|
|
413
|
+
*/
|
|
414
|
+
async RefreshActions(contextUser) {
|
|
299
415
|
try {
|
|
300
|
-
await
|
|
301
|
-
const actions =
|
|
416
|
+
await ActionEngineBase.Instance.Config(false, contextUser);
|
|
417
|
+
const actions = ActionEngineBase.Instance.Actions.filter(a => a.Status === 'Active');
|
|
302
418
|
if (actions && actions.length > 0) {
|
|
303
419
|
this._actions = actions;
|
|
304
420
|
}
|
|
305
421
|
else {
|
|
306
|
-
|
|
422
|
+
LogError('AIEngine: No active actions found during load');
|
|
307
423
|
this._actions = [];
|
|
308
424
|
}
|
|
309
425
|
}
|
|
310
426
|
catch (error) {
|
|
311
|
-
|
|
427
|
+
LogError(`AIEngine: Error loading actions: ${error instanceof Error ? error.message : String(error)}`);
|
|
312
428
|
this._actions = [];
|
|
313
429
|
}
|
|
314
430
|
}
|
|
315
|
-
|
|
431
|
+
/**
|
|
432
|
+
* Dynamically calculation of embeddings for all `Active` actions. Assumes that the internal Actions array is up to date, call
|
|
433
|
+
* @see RefreshActions first if you do not think they are already.
|
|
434
|
+
*
|
|
435
|
+
* This operation dynamically calculates embeddings from the text in the Action metadata and is an expensive operation, use it
|
|
436
|
+
* sparingly.
|
|
437
|
+
*/
|
|
438
|
+
async RefreshActionEmbeddings() {
|
|
316
439
|
try {
|
|
317
440
|
const actions = this._actions;
|
|
318
441
|
if (!actions || actions.length === 0) {
|
|
319
442
|
return;
|
|
320
443
|
}
|
|
444
|
+
// Filter to only actions that don't have embeddings yet
|
|
321
445
|
const actionsNeedingEmbeddings = actions.filter(action => !this._actionEmbeddingsCache.has(action.ID));
|
|
322
446
|
if (actionsNeedingEmbeddings.length === 0) {
|
|
323
447
|
return;
|
|
324
448
|
}
|
|
325
|
-
|
|
449
|
+
// Generate embeddings using static utility method
|
|
450
|
+
const entries = await ActionEmbeddingService.GenerateActionEmbeddings(actionsNeedingEmbeddings, (text) => this.EmbedTextLocal(text));
|
|
451
|
+
// Mark these actions as having embeddings
|
|
326
452
|
for (const action of actionsNeedingEmbeddings) {
|
|
327
453
|
this._actionEmbeddingsCache.set(action.ID, true);
|
|
328
454
|
}
|
|
455
|
+
// Load into vector service (create if needed, or add to existing)
|
|
329
456
|
if (!this._actionVectorService) {
|
|
330
|
-
this._actionVectorService = new
|
|
457
|
+
this._actionVectorService = new SimpleVectorService();
|
|
331
458
|
}
|
|
332
459
|
this._actionVectorService.LoadVectors(entries);
|
|
333
460
|
}
|
|
334
461
|
catch (error) {
|
|
335
|
-
|
|
462
|
+
LogError(`AIEngine: Failed to load action embeddings: ${error instanceof Error ? error.message : String(error)}`);
|
|
463
|
+
// Don't throw - allow AIEngine to continue loading even if embeddings fail
|
|
336
464
|
}
|
|
337
465
|
}
|
|
338
|
-
|
|
466
|
+
/**
|
|
467
|
+
* Refresh the vector service with the latest persisted vectors that are stored in the Agent Notes
|
|
468
|
+
* table. This does **not** calculate embeddings, that is done by the AI Agent Note sub-class upon save
|
|
469
|
+
* as needed. This method simply uses the stored vectors and parses them from their JSON serialized format into
|
|
470
|
+
* vectors that are used by the vector service.
|
|
471
|
+
*/
|
|
472
|
+
async RefreshNoteEmbeddings(contextUser) {
|
|
339
473
|
try {
|
|
340
474
|
const notes = this.AgentNotes.filter(n => n.Status === 'Active' && n.EmbeddingVector);
|
|
341
475
|
if (notes.length === 0) {
|
|
@@ -344,24 +478,78 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
344
478
|
const entries = notes.map(note => ({
|
|
345
479
|
key: note.ID,
|
|
346
480
|
vector: JSON.parse(note.EmbeddingVector),
|
|
347
|
-
metadata:
|
|
348
|
-
id: note.ID,
|
|
349
|
-
agentId: note.AgentID,
|
|
350
|
-
userId: note.UserID,
|
|
351
|
-
companyId: note.CompanyID,
|
|
352
|
-
type: note.Type,
|
|
353
|
-
noteText: note.Note,
|
|
354
|
-
noteEntity: note
|
|
355
|
-
}
|
|
481
|
+
metadata: this.packageNoteMetadata(note)
|
|
356
482
|
}));
|
|
357
|
-
this._noteVectorService = new
|
|
483
|
+
this._noteVectorService = new SimpleVectorService();
|
|
358
484
|
this._noteVectorService.LoadVectors(entries);
|
|
359
485
|
}
|
|
360
486
|
catch (error) {
|
|
361
|
-
|
|
487
|
+
LogError(`AIEngine: Failed to load note embeddings: ${error instanceof Error ? error.message : String(error)}`);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Takes in a note and packages up the metadata for the vector service
|
|
492
|
+
* @param note
|
|
493
|
+
*/
|
|
494
|
+
packageNoteMetadata(note) {
|
|
495
|
+
return {
|
|
496
|
+
id: note.ID,
|
|
497
|
+
agentId: note.AgentID,
|
|
498
|
+
userId: note.UserID,
|
|
499
|
+
companyId: note.CompanyID,
|
|
500
|
+
type: note.Type,
|
|
501
|
+
noteText: note.Note,
|
|
502
|
+
noteEntity: note
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Updates the vector service to the latest vector containd within the specified agent note that is passed in
|
|
507
|
+
* @param note
|
|
508
|
+
*/
|
|
509
|
+
AddOrUpdateSingleNoteEmbedding(note) {
|
|
510
|
+
if (this._noteVectorService) {
|
|
511
|
+
this._noteVectorService.AddOrUpdateVector(note.ID, JSON.parse(note.EmbeddingVector), this.packageNoteMetadata(note));
|
|
512
|
+
}
|
|
513
|
+
else {
|
|
514
|
+
throw new Error('note vector service not initialized, error state');
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Takes in an example and packages up the metadata for the vector service
|
|
519
|
+
* @param example
|
|
520
|
+
*/
|
|
521
|
+
packageExampleMetadata(example) {
|
|
522
|
+
return {
|
|
523
|
+
id: example.ID,
|
|
524
|
+
agentId: example.AgentID,
|
|
525
|
+
userId: example.UserID,
|
|
526
|
+
companyId: example.CompanyID,
|
|
527
|
+
type: example.Type,
|
|
528
|
+
exampleInput: example.ExampleInput,
|
|
529
|
+
exampleOutput: example.ExampleOutput,
|
|
530
|
+
successScore: example.SuccessScore,
|
|
531
|
+
exampleEntity: example
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Updates the vector service to the latest vector contained within the specified agent example that is passed in
|
|
536
|
+
* @param example
|
|
537
|
+
*/
|
|
538
|
+
AddOrUpdateSingleExampleEmbedding(example) {
|
|
539
|
+
if (this._exampleVectorService) {
|
|
540
|
+
this._exampleVectorService.AddOrUpdateVector(example.ID, JSON.parse(example.EmbeddingVector), this.packageExampleMetadata(example));
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
throw new Error('example vector service not initialized, error state');
|
|
362
544
|
}
|
|
363
545
|
}
|
|
364
|
-
|
|
546
|
+
/**
|
|
547
|
+
* Refresh the vector service with the latest persisted vectors that are stored in the Agent Examples
|
|
548
|
+
* table. This does **not** calculate embeddings, that is done by the AI Agent Example sub-class upon save
|
|
549
|
+
* as needed. This method simply uses the stored vectors and parses them from their JSON serialized format into
|
|
550
|
+
* vectors that are used by the vector service.
|
|
551
|
+
*/
|
|
552
|
+
async RefreshExampleEmbeddings(contextUser) {
|
|
365
553
|
try {
|
|
366
554
|
const examples = this.AgentExamples.filter(e => e.Status === 'Active' && e.EmbeddingVector);
|
|
367
555
|
if (examples.length === 0) {
|
|
@@ -370,73 +558,64 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
370
558
|
const entries = examples.map(example => ({
|
|
371
559
|
key: example.ID,
|
|
372
560
|
vector: JSON.parse(example.EmbeddingVector),
|
|
373
|
-
metadata:
|
|
374
|
-
id: example.ID,
|
|
375
|
-
agentId: example.AgentID,
|
|
376
|
-
userId: example.UserID,
|
|
377
|
-
companyId: example.CompanyID,
|
|
378
|
-
type: example.Type,
|
|
379
|
-
exampleInput: example.ExampleInput,
|
|
380
|
-
exampleOutput: example.ExampleOutput,
|
|
381
|
-
successScore: example.SuccessScore,
|
|
382
|
-
exampleEntity: example
|
|
383
|
-
}
|
|
561
|
+
metadata: this.packageExampleMetadata(example)
|
|
384
562
|
}));
|
|
385
|
-
this._exampleVectorService = new
|
|
563
|
+
this._exampleVectorService = new SimpleVectorService();
|
|
386
564
|
this._exampleVectorService.LoadVectors(entries);
|
|
387
565
|
}
|
|
388
566
|
catch (error) {
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
async generateSingleAgentEmbedding(agent) {
|
|
393
|
-
try {
|
|
394
|
-
if (agent.IsRestricted) {
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
const entries = await AgentEmbeddingService_1.AgentEmbeddingService.GenerateAgentEmbeddings([agent], (text) => this.EmbedTextLocal(text));
|
|
398
|
-
if (entries.length > 0) {
|
|
399
|
-
this._agentEmbeddingsCache.set(agent.ID, true);
|
|
400
|
-
if (!this._agentVectorService) {
|
|
401
|
-
this._agentVectorService = new ai_vectors_memory_1.SimpleVectorService();
|
|
402
|
-
}
|
|
403
|
-
this._agentVectorService.LoadVectors(entries);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
catch (error) {
|
|
407
|
-
(0, core_1.LogError)(`AIEngine: Failed to generate embedding for agent ${agent.Name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
async generateSingleActionEmbedding(action) {
|
|
411
|
-
try {
|
|
412
|
-
const entries = await ActionEmbeddingService_1.ActionEmbeddingService.GenerateActionEmbeddings([action], (text) => this.EmbedTextLocal(text));
|
|
413
|
-
if (entries.length > 0) {
|
|
414
|
-
this._actionEmbeddingsCache.set(action.ID, true);
|
|
415
|
-
if (!this._actionVectorService) {
|
|
416
|
-
this._actionVectorService = new ai_vectors_memory_1.SimpleVectorService();
|
|
417
|
-
}
|
|
418
|
-
this._actionVectorService.LoadVectors(entries);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
catch (error) {
|
|
422
|
-
(0, core_1.LogError)(`AIEngine: Failed to generate embedding for action ${action.Name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
567
|
+
LogError(`AIEngine: Failed to load example embeddings: ${error instanceof Error ? error.message : String(error)}`);
|
|
423
568
|
}
|
|
424
569
|
}
|
|
570
|
+
// ========================================================================
|
|
571
|
+
// LLM Utility Methods
|
|
572
|
+
// ========================================================================
|
|
573
|
+
/**
|
|
574
|
+
* Prepares standard chat parameters with system and user messages.
|
|
575
|
+
*
|
|
576
|
+
* @param userPrompt The user message/query to send to the model
|
|
577
|
+
* @param systemPrompt Optional system prompt to set context/persona for the model
|
|
578
|
+
* @returns Array of properly formatted chat messages
|
|
579
|
+
*/
|
|
425
580
|
PrepareChatMessages(userPrompt, systemPrompt) {
|
|
426
581
|
const messages = [];
|
|
427
582
|
if (systemPrompt && systemPrompt.length > 0) {
|
|
428
|
-
messages.push({ role:
|
|
583
|
+
messages.push({ role: ChatMessageRole.system, content: systemPrompt });
|
|
429
584
|
}
|
|
430
|
-
messages.push({ role:
|
|
585
|
+
messages.push({ role: ChatMessageRole.user, content: userPrompt });
|
|
431
586
|
return messages;
|
|
432
587
|
}
|
|
588
|
+
/**
|
|
589
|
+
* Prepares an LLM model instance with the appropriate parameters.
|
|
590
|
+
* This method handles common tasks needed before calling an LLM:
|
|
591
|
+
* - Loading AI metadata if needed
|
|
592
|
+
* - Selecting the appropriate model (user-provided or highest power)
|
|
593
|
+
* - Getting the correct API key
|
|
594
|
+
* - Creating the LLM instance
|
|
595
|
+
*
|
|
596
|
+
* @param contextUser The user context for authentication and permissions
|
|
597
|
+
* @param model Optional specific model to use, otherwise uses highest power LLM
|
|
598
|
+
* @param apiKey Optional API key to use with the model
|
|
599
|
+
* @returns Object containing the prepared model instance and model information
|
|
600
|
+
*/
|
|
433
601
|
async PrepareLLMInstance(contextUser, model, apiKey) {
|
|
434
602
|
await AIEngine.Instance.Config(false, contextUser);
|
|
435
603
|
const modelToUse = model ? model : await this.GetHighestPowerLLM(undefined, contextUser);
|
|
436
|
-
const apiKeyToUse = apiKey ? apiKey :
|
|
437
|
-
const modelInstance =
|
|
604
|
+
const apiKeyToUse = apiKey ? apiKey : GetAIAPIKey(modelToUse.DriverClass);
|
|
605
|
+
const modelInstance = MJGlobal.Instance.ClassFactory.CreateInstance(BaseLLM, modelToUse.DriverClass, apiKeyToUse);
|
|
438
606
|
return { modelInstance, modelToUse };
|
|
439
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* Executes a simple completion task using the provided parameters.
|
|
610
|
+
*
|
|
611
|
+
* @param userPrompt The user message/query to send to the model
|
|
612
|
+
* @param contextUser The user context for authentication and permissions
|
|
613
|
+
* @param systemPrompt Optional system prompt to set context/persona for the model
|
|
614
|
+
* @param model Optional specific model to use, otherwise uses highest power LLM
|
|
615
|
+
* @param apiKey Optional API key to use with the model
|
|
616
|
+
* @returns The text response from the LLM
|
|
617
|
+
* @throws Error if user prompt is not provided or if there are issues with model creation
|
|
618
|
+
*/
|
|
440
619
|
async SimpleLLMCompletion(userPrompt, contextUser, systemPrompt, model, apiKey) {
|
|
441
620
|
try {
|
|
442
621
|
if (!userPrompt || userPrompt.length === 0) {
|
|
@@ -444,7 +623,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
444
623
|
}
|
|
445
624
|
const { modelInstance, modelToUse } = await this.PrepareLLMInstance(contextUser, model, apiKey);
|
|
446
625
|
const messages = this.PrepareChatMessages(userPrompt, systemPrompt);
|
|
447
|
-
const params = new
|
|
626
|
+
const params = new ChatParams();
|
|
448
627
|
params.messages = messages;
|
|
449
628
|
params.model = modelToUse.APIName;
|
|
450
629
|
const result = await modelInstance.ChatCompletion(params);
|
|
@@ -455,10 +634,24 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
455
634
|
throw new Error(`Error executing LLM model ${modelToUse.Name} : ${result.errorMessage}`);
|
|
456
635
|
}
|
|
457
636
|
catch (e) {
|
|
458
|
-
|
|
637
|
+
LogError(e);
|
|
459
638
|
throw e;
|
|
460
639
|
}
|
|
461
640
|
}
|
|
641
|
+
/**
|
|
642
|
+
* Executes multiple parallel chat completions with the same model but potentially different parameters.
|
|
643
|
+
*
|
|
644
|
+
* @param userPrompt The user's message/question to send to the model
|
|
645
|
+
* @param contextUser The user context for authentication and logging
|
|
646
|
+
* @param systemPrompt Optional system prompt to set the context/persona
|
|
647
|
+
* @param iterations Number of parallel completions to run (default: 3)
|
|
648
|
+
* @param temperatureIncrement The amount to increment temperature for each iteration (default: 0.1)
|
|
649
|
+
* @param baseTemperature The starting temperature value (default: 0.7)
|
|
650
|
+
* @param model Optional specific model to use, otherwise uses highest power LLM
|
|
651
|
+
* @param apiKey Optional API key to use with the model
|
|
652
|
+
* @param callbacks Optional callbacks for monitoring progress
|
|
653
|
+
* @returns Array of ChatResult objects, one for each parallel completion
|
|
654
|
+
*/
|
|
462
655
|
async ParallelLLMCompletions(userPrompt, contextUser, systemPrompt, iterations = 3, temperatureIncrement = 0.1, baseTemperature = 0.7, model, apiKey, callbacks) {
|
|
463
656
|
try {
|
|
464
657
|
if (!userPrompt || userPrompt.length === 0) {
|
|
@@ -470,7 +663,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
470
663
|
const { modelInstance, modelToUse } = await this.PrepareLLMInstance(contextUser, model, apiKey);
|
|
471
664
|
const messages = this.PrepareChatMessages(userPrompt, systemPrompt);
|
|
472
665
|
const paramsArray = Array(iterations).fill(0).map((_, i) => {
|
|
473
|
-
const params = new
|
|
666
|
+
const params = new ChatParams();
|
|
474
667
|
params.messages = [...messages];
|
|
475
668
|
params.model = modelToUse.APIName;
|
|
476
669
|
params.temperature = baseTemperature + (i * temperatureIncrement);
|
|
@@ -479,12 +672,19 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
479
672
|
return await modelInstance.ChatCompletions(paramsArray, callbacks);
|
|
480
673
|
}
|
|
481
674
|
catch (e) {
|
|
482
|
-
|
|
675
|
+
LogError(e);
|
|
483
676
|
throw e;
|
|
484
677
|
}
|
|
485
678
|
}
|
|
679
|
+
// ========================================================================
|
|
680
|
+
// Embedding Methods
|
|
681
|
+
// ========================================================================
|
|
682
|
+
/**
|
|
683
|
+
* Returns an array of the local embedding models, sorted with the highest power models first
|
|
684
|
+
*/
|
|
486
685
|
get LocalEmbeddingModels() {
|
|
487
686
|
const embeddingModels = this.Models.filter(m => {
|
|
687
|
+
// Guard against AIModelType being non-string (defensive coding for data issues)
|
|
488
688
|
const modelType = typeof m.AIModelType === 'string' ? m.AIModelType.trim().toLowerCase() : '';
|
|
489
689
|
const vendor = typeof m.Vendor === 'string' ? m.Vendor.trim().toLowerCase() : '';
|
|
490
690
|
return modelType === this.EmbeddingModelTypeName.toLowerCase() &&
|
|
@@ -492,51 +692,78 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
492
692
|
});
|
|
493
693
|
return embeddingModels.sort((a, b) => (b.PowerRank || 0) - (a.PowerRank || 0));
|
|
494
694
|
}
|
|
695
|
+
/**
|
|
696
|
+
* Returns the highest power local embedding model
|
|
697
|
+
*/
|
|
495
698
|
get HighestPowerLocalEmbeddingModel() {
|
|
496
699
|
const models = this.LocalEmbeddingModels;
|
|
497
700
|
return models && models.length > 0 ? models[0] : null;
|
|
498
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* Returns the lowest power local embedding model
|
|
704
|
+
*/
|
|
499
705
|
get LowestPowerLocalEmbeddingModel() {
|
|
500
706
|
const models = this.LocalEmbeddingModels;
|
|
501
707
|
return models && models.length > 0 ? models[models.length - 1] : null;
|
|
502
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Helper method that generates an embedding for the given text using the highest power local embedding model.
|
|
711
|
+
*/
|
|
503
712
|
async EmbedTextLocal(text) {
|
|
504
713
|
const model = this.HighestPowerLocalEmbeddingModel;
|
|
505
714
|
if (!model) {
|
|
506
|
-
|
|
715
|
+
LogError('AIEngine: No local embedding model found. Cannot generate embedding.');
|
|
507
716
|
return null;
|
|
508
717
|
}
|
|
509
718
|
const result = await this.EmbedText(model, text);
|
|
510
719
|
return { result, model };
|
|
511
720
|
}
|
|
721
|
+
/**
|
|
722
|
+
* Helper method to instantiate a class instance for the given model and calculate an embedding
|
|
723
|
+
* vector from the provided text.
|
|
724
|
+
*/
|
|
512
725
|
async EmbedText(model, text, apiKey) {
|
|
513
726
|
const params = {
|
|
514
727
|
text: text,
|
|
515
728
|
model: model.APIName
|
|
516
729
|
};
|
|
517
|
-
const embedding =
|
|
730
|
+
const embedding = MJGlobal.Instance.ClassFactory.CreateInstance(BaseEmbeddings, model.DriverClass, apiKey);
|
|
518
731
|
if (!embedding) {
|
|
519
|
-
|
|
732
|
+
LogError(`AIEngine: Failed to create embedding instance for model ${model.Name}. Skipping embedding generation.`);
|
|
520
733
|
return null;
|
|
521
734
|
}
|
|
522
735
|
const result = await embedding.EmbedText(params);
|
|
523
736
|
return result;
|
|
524
737
|
}
|
|
738
|
+
// ========================================================================
|
|
739
|
+
// Semantic Search Methods
|
|
740
|
+
// ========================================================================
|
|
741
|
+
/**
|
|
742
|
+
* Find agents similar to a task description using semantic search.
|
|
743
|
+
*/
|
|
525
744
|
async FindSimilarAgents(taskDescription, topK = 5, minSimilarity = 0.5) {
|
|
526
745
|
if (!this._agentVectorService) {
|
|
527
746
|
throw new Error('Agent embeddings not loaded. Ensure AIEngine.Config() has completed.');
|
|
528
747
|
}
|
|
529
|
-
return
|
|
748
|
+
return AgentEmbeddingService.FindSimilarAgents(this._agentVectorService, taskDescription, (text) => this.EmbedTextLocal(text), topK, minSimilarity);
|
|
530
749
|
}
|
|
750
|
+
/**
|
|
751
|
+
* Find actions similar to a task description using semantic search.
|
|
752
|
+
*/
|
|
531
753
|
async FindSimilarActions(taskDescription, topK = 10, minSimilarity = 0.5) {
|
|
532
754
|
if (!this._actionVectorService) {
|
|
533
755
|
throw new Error('Action embeddings not loaded. Ensure AIEngine.Config() has completed.');
|
|
534
756
|
}
|
|
535
|
-
return
|
|
757
|
+
return ActionEmbeddingService.FindSimilarActions(this._actionVectorService, taskDescription, (text) => this.EmbedTextLocal(text), topK, minSimilarity);
|
|
536
758
|
}
|
|
759
|
+
/**
|
|
760
|
+
* Find notes similar to query text using semantic search.
|
|
761
|
+
* Falls back to returning notes from cache if vector service is unavailable.
|
|
762
|
+
*/
|
|
537
763
|
async FindSimilarAgentNotes(queryText, agentId, userId, companyId, topK = 5, minSimilarity = 0.5) {
|
|
538
764
|
if (!this._noteVectorService) {
|
|
539
|
-
|
|
765
|
+
// Vector service not available - fall back to returning notes from cache filtered by scope
|
|
766
|
+
LogError('FindSimilarAgentNotes: Note vector service not initialized. Falling back to cached notes without semantic ranking.');
|
|
540
767
|
return this.fallbackGetNotesFromCache(agentId, userId, companyId, topK);
|
|
541
768
|
}
|
|
542
769
|
if (!queryText || queryText.trim().length === 0) {
|
|
@@ -544,7 +771,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
544
771
|
}
|
|
545
772
|
const queryEmbedding = await this.EmbedTextLocal(queryText);
|
|
546
773
|
if (!queryEmbedding || !queryEmbedding.result || queryEmbedding.result.vector.length === 0) {
|
|
547
|
-
|
|
774
|
+
LogError('FindSimilarAgentNotes: Failed to generate embedding for query text. Falling back to cached notes.');
|
|
548
775
|
return this.fallbackGetNotesFromCache(agentId, userId, companyId, topK);
|
|
549
776
|
}
|
|
550
777
|
const needsFiltering = agentId || userId || companyId;
|
|
@@ -563,6 +790,10 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
563
790
|
similarity: r.score
|
|
564
791
|
}));
|
|
565
792
|
}
|
|
793
|
+
/**
|
|
794
|
+
* Fallback method to get notes from cache when vector service is unavailable.
|
|
795
|
+
* Returns notes filtered by scope, sorted by creation date.
|
|
796
|
+
*/
|
|
566
797
|
fallbackGetNotesFromCache(agentId, userId, companyId, topK = 5) {
|
|
567
798
|
const notes = this.AgentNotes.filter(n => {
|
|
568
799
|
if (n.Status !== 'Active')
|
|
@@ -575,17 +806,24 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
575
806
|
return false;
|
|
576
807
|
return true;
|
|
577
808
|
});
|
|
809
|
+
// Sort by creation date (most recent first) and take topK
|
|
578
810
|
const sorted = notes
|
|
579
811
|
.sort((a, b) => (b.__mj_CreatedAt?.getTime() || 0) - (a.__mj_CreatedAt?.getTime() || 0))
|
|
580
812
|
.slice(0, topK);
|
|
813
|
+
// Return with similarity of 0 to indicate no semantic ranking was applied
|
|
581
814
|
return sorted.map(note => ({
|
|
582
815
|
note,
|
|
583
816
|
similarity: 0
|
|
584
817
|
}));
|
|
585
818
|
}
|
|
819
|
+
/**
|
|
820
|
+
* Find examples similar to query text using semantic search.
|
|
821
|
+
* Falls back to returning examples from cache if vector service is unavailable.
|
|
822
|
+
*/
|
|
586
823
|
async FindSimilarAgentExamples(queryText, agentId, userId, companyId, topK = 3, minSimilarity = 0.5) {
|
|
587
824
|
if (!this._exampleVectorService) {
|
|
588
|
-
|
|
825
|
+
// Vector service not available - fall back to returning examples from cache filtered by scope
|
|
826
|
+
LogError('FindSimilarAgentExamples: Example vector service not initialized. Falling back to cached examples without semantic ranking.');
|
|
589
827
|
return this.fallbackGetExamplesFromCache(agentId, userId, companyId, topK);
|
|
590
828
|
}
|
|
591
829
|
if (!queryText || queryText.trim().length === 0) {
|
|
@@ -593,7 +831,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
593
831
|
}
|
|
594
832
|
const queryEmbedding = await this.EmbedTextLocal(queryText);
|
|
595
833
|
if (!queryEmbedding || !queryEmbedding.result || queryEmbedding.result.vector.length === 0) {
|
|
596
|
-
|
|
834
|
+
LogError('FindSimilarAgentExamples: Failed to generate embedding for query text. Falling back to cached examples.');
|
|
597
835
|
return this.fallbackGetExamplesFromCache(agentId, userId, companyId, topK);
|
|
598
836
|
}
|
|
599
837
|
const needsFiltering = agentId || userId || companyId;
|
|
@@ -612,6 +850,10 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
612
850
|
similarity: r.score
|
|
613
851
|
}));
|
|
614
852
|
}
|
|
853
|
+
/**
|
|
854
|
+
* Fallback method to get examples from cache when vector service is unavailable.
|
|
855
|
+
* Returns examples filtered by scope, sorted by success score then creation date.
|
|
856
|
+
*/
|
|
615
857
|
fallbackGetExamplesFromCache(agentId, userId, companyId, topK = 3) {
|
|
616
858
|
const examples = this.AgentExamples.filter(e => {
|
|
617
859
|
if (e.Status !== 'Active')
|
|
@@ -624,6 +866,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
624
866
|
return false;
|
|
625
867
|
return true;
|
|
626
868
|
});
|
|
869
|
+
// Sort by success score (highest first), then by creation date (most recent first)
|
|
627
870
|
const sorted = examples
|
|
628
871
|
.sort((a, b) => {
|
|
629
872
|
const scoreA = a.SuccessScore ?? 0;
|
|
@@ -633,11 +876,18 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
633
876
|
return (b.__mj_CreatedAt?.getTime() || 0) - (a.__mj_CreatedAt?.getTime() || 0);
|
|
634
877
|
})
|
|
635
878
|
.slice(0, topK);
|
|
879
|
+
// Return with similarity of 0 to indicate no semantic ranking was applied
|
|
636
880
|
return sorted.map(example => ({
|
|
637
881
|
example,
|
|
638
882
|
similarity: 0
|
|
639
883
|
}));
|
|
640
884
|
}
|
|
885
|
+
// ========================================================================
|
|
886
|
+
// Deprecated AI Action Methods
|
|
887
|
+
// ========================================================================
|
|
888
|
+
/**
|
|
889
|
+
* @deprecated Entity AI Actions are deprecated. Use AIPromptRunner with the new AI Prompt system instead.
|
|
890
|
+
*/
|
|
641
891
|
async ExecuteEntityAIAction(params) {
|
|
642
892
|
const startTime = new Date();
|
|
643
893
|
try {
|
|
@@ -664,7 +914,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
664
914
|
modelId: modelId,
|
|
665
915
|
systemPrompt: entityPrompt,
|
|
666
916
|
userMessage: userMessage,
|
|
667
|
-
apiKey:
|
|
917
|
+
apiKey: GetAIAPIKey(model.DriverClass),
|
|
668
918
|
result: null
|
|
669
919
|
};
|
|
670
920
|
if (!await params.entityRecord.BeforeEntityAIAction(entityParams))
|
|
@@ -691,10 +941,11 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
691
941
|
}
|
|
692
942
|
}
|
|
693
943
|
else if (entityAction.OutputType.trim().toLowerCase() === 'entity') {
|
|
694
|
-
const md = new
|
|
944
|
+
const md = new Metadata();
|
|
695
945
|
const newRecord = await md.GetEntityObject(entityAction.OutputEntity);
|
|
696
946
|
newRecord.NewRecord();
|
|
697
947
|
newRecord.Set('EntityID', params.entityRecord.EntityInfo.ID);
|
|
948
|
+
// Use concatenated key format to properly support both single and composite primary keys
|
|
698
949
|
newRecord.Set('RecordID', params.entityRecord.PrimaryKey.ToConcatenatedString());
|
|
699
950
|
newRecord.Set(entityAction.OutputField, sOutput);
|
|
700
951
|
await newRecord.Save();
|
|
@@ -703,7 +954,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
703
954
|
return results;
|
|
704
955
|
}
|
|
705
956
|
catch (err) {
|
|
706
|
-
|
|
957
|
+
LogError('AIEngine: ExecuteEntityAIAction failed', undefined, err instanceof Error ? err : undefined);
|
|
707
958
|
return {
|
|
708
959
|
success: false,
|
|
709
960
|
startTime: startTime,
|
|
@@ -726,6 +977,9 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
726
977
|
}
|
|
727
978
|
return temp;
|
|
728
979
|
}
|
|
980
|
+
/**
|
|
981
|
+
* @deprecated AI Actions are deprecated. Use AIPromptRunner with the new AI Prompt system instead.
|
|
982
|
+
*/
|
|
729
983
|
async ExecuteAIAction(params) {
|
|
730
984
|
const action = this.Actions.find(a => a.ID === params.actionId);
|
|
731
985
|
if (!action)
|
|
@@ -737,7 +991,7 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
737
991
|
throw new Error(`Model ${params.modelId} not found.`);
|
|
738
992
|
if (model.IsActive === false)
|
|
739
993
|
throw new Error(`Model ${params.modelId} is not active.`);
|
|
740
|
-
const driver = await this.getDriver(model,
|
|
994
|
+
const driver = await this.getDriver(model, GetAIAPIKey(model.DriverClass));
|
|
741
995
|
if (driver) {
|
|
742
996
|
const modelParams = {
|
|
743
997
|
model: params.modelName,
|
|
@@ -769,6 +1023,9 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
769
1023
|
else
|
|
770
1024
|
throw new Error(`Driver ${model.DriverClass} not found or couldn't be loaded.`);
|
|
771
1025
|
}
|
|
1026
|
+
/**
|
|
1027
|
+
* @deprecated This method is related to deprecated AI Actions. Use AIPromptRunner instead.
|
|
1028
|
+
*/
|
|
772
1029
|
GetStringOutputFromActionResults(action, result) {
|
|
773
1030
|
switch (action.Name.trim().toLowerCase()) {
|
|
774
1031
|
case 'classify':
|
|
@@ -789,20 +1046,15 @@ class AIEngine extends global_1.BaseSingleton {
|
|
|
789
1046
|
const driverModuleName = model.DriverImportPath;
|
|
790
1047
|
try {
|
|
791
1048
|
if (driverModuleName && driverModuleName.length > 0) {
|
|
792
|
-
const driverModule = await
|
|
1049
|
+
const driverModule = await import(driverModuleName);
|
|
793
1050
|
if (!driverModule)
|
|
794
1051
|
throw new Error(`Error loading driver module '${driverModuleName}'`);
|
|
795
1052
|
}
|
|
796
|
-
return
|
|
1053
|
+
return MJGlobal.Instance.ClassFactory.CreateInstance(BaseModel, driverClassName, apiKey);
|
|
797
1054
|
}
|
|
798
1055
|
catch (e) {
|
|
799
1056
|
throw new Error(`Error loading driver '${driverModuleName}' / '${driverClassName}' : ${e.message}`);
|
|
800
1057
|
}
|
|
801
1058
|
}
|
|
802
1059
|
}
|
|
803
|
-
exports.AIEngine = AIEngine;
|
|
804
|
-
function LoadAIEngine() {
|
|
805
|
-
(0, ai_engine_base_1.LoadBaseAIEngine)();
|
|
806
|
-
}
|
|
807
|
-
exports.LoadAIEngine = LoadAIEngine;
|
|
808
1060
|
//# sourceMappingURL=AIEngine.js.map
|